
    =iI(                         S r SSKrSSKJrJr  SSKJrJr  SSKJ	r	J
r
JrJrJr  SSKJr  SSKJrJr  SSKJr  SS	KJr  SS
KJrJr  SSKJrJr  \\\   /\4   rSrSr Sr!Sr"Sr#Sr$ " S S\5      r%g)zSummarization middleware.    N)CallableIterable)Anycast)	AIMessage
AnyMessageMessageLikeRepresentationRemoveMessageToolMessageHumanMessage)count_tokens_approximatelytrim_messages)REMOVE_ALL_MESSAGES)Runtime)AgentMiddleware
AgentState)BaseChatModelinit_chat_modela  <role>
Context Extraction Assistant
</role>

<primary_objective>
Your sole objective in this task is to extract the highest quality/most relevant context from the conversation history below.
</primary_objective>

<objective_information>
You're nearing the total number of input tokens you can accept, so you must extract the highest quality/most relevant pieces of information from your conversation history.
This context will then overwrite the conversation history presented below. Because of this, ensure the context you extract is only the most important information to your overall goal.
</objective_information>

<instructions>
The conversation history below will be replaced with the context you extract in this step. Because of this, you must do your very best to extract and record all of the most important context from the conversation history.
You want to ensure that you don't repeat any actions you've already completed, so the context you extract from the conversation history should be focused on the most important information to your overall goal.
</instructions>

The user will message you with the full message history you'll be extracting context from, to then replace. Carefully read over it all, and think deeply about what information is most important to your overall goal that should be saved:

With all of this in mind, please carefully read over the entire conversation history, and extract the most important and relevant context to replace it so that you can free up space in the conversation history.
Respond ONLY with the extracted context. Do not include any additional information, or text before or after the extracted context.

<messages>
Messages to summarize:
{messages}
</messages>z!## Previous conversation summary:   i        c                     ^  \ rS rSrSrS\\\\4S\	\
-  S\S-  S\S\S\	S	\	S
S4U 4S jjjrS\S\S
\\	\4   S-  4S jrS\	S
\\   4S jrS\\   S
S4S jrS\\   S\S
\\\   \\   4   4S jrS\\   S
\4S jrS\\   S\S
\4S jrS\S
\4S jrS\S
\\	   4S jr S\\   S\S\S\\	   S
\4
S jr!S\\   S
\	4S  jr"S\\   S
\\   4S! jr#S"r$U =r%$ )#SummarizationMiddleware>   a+  Summarizes conversation history when token limits are approached.

This middleware monitors message token counts and automatically summarizes older
messages when a threshold is reached, preserving recent messages and maintaining
context continuity by ensuring AI/Tool message pairs remain together.
Nmodelmax_tokens_before_summarymessages_to_keeptoken_countersummary_promptsummary_prefixreturnc                    > [         TU ]  5         [        U[        5      (       a  [	        U5      nXl        X l        X0l        X@l        XPl	        X`l
        g)a  Initialize the summarization middleware.

Args:
    model: The language model to use for generating summaries.
    max_tokens_before_summary: Token threshold to trigger summarization.
        If `None`, summarization is disabled.
    messages_to_keep: Number of recent messages to preserve after summarization.
    token_counter: Function to count tokens in messages.
    summary_prompt: Prompt template for generating summaries.
    summary_prefix: Prefix added to system message when including summary.
N)super__init__
isinstancestrr   r   r   r   r   r    r!   )selfr   r   r   r   r    r!   	__class__s          y/home/dmtnaga/Documents/work/airagagent/rag_env/lib/python3.13/site-packages/langchain/agents/middleware/summarization.pyr%    SummarizationMiddleware.__init__F   sJ    ( 	eS!!#E*E
)B& 0*,,    stateruntimec                 L   US   nU R                  U5        U R                  U5      nU R                  b  X@R                  :  a  gU R                  U5      nUS::  a  gU R	                  X55      u  pgU R                  U5      nU R                  U5      n	S[        [        S9/U	QUQ0$ )zOProcess messages before model invocation, potentially triggering summarization.messagesNr   )id)	_ensure_message_idsr   r   _find_safe_cutoff_partition_messages_create_summary_build_new_messagesr
   r   )
r(   r-   r.   r0   total_tokenscutoff_indexmessages_to_summarizepreserved_messagessummarynew_messagess
             r*   before_model$SummarizationMiddleware.before_modelf   s    $  *))(3**6===--h71484L4LX4d1&&'<=//8 !45 $
 	
r,   r;   c                     [        SU 3S9/$ )Nz0Here is a summary of the conversation to date:

)contentr   )r(   r;   s     r*   r6   +SummarizationMiddleware._build_new_messages   s    #UV]U^!_`
 	
r,   r0   c                 z    U H5  nUR                   b  M  [        [        R                  " 5       5      Ul         M7     g)zAEnsure all messages have unique IDs for the add_messages reducer.N)r1   r'   uuiduuid4)r(   r0   msgs      r*   r2   +SummarizationMiddleware._ensure_message_ids   s'    Cvv~TZZ\* r,   conversation_messagesr8   c                     USU nXS nX44$ )zAPartition messages into those to summarize and those to preserve.N )r(   rG   r8   r9   r:   s        r*   r4   +SummarizationMiddleware._partition_messages   s%     !6m| D2=A$88r,   c                     [        U5      U R                  ::  a  g[        U5      U R                  -
  n[        USS5       H  nU R                  X5      (       d  M  Us  $    g)zFind safe cutoff point that preserves AI/Tool message pairs.

Returns the index where messages can be safely cut without separating
related AI and Tool messages. Returns 0 if no safe cutoff is found.
r   )lenr   range_is_safe_cutoff_point)r(   r0   target_cutoffis       r*   r3   )SummarizationMiddleware._find_safe_cutoff   s[     x=D111H(=(==}b"-A))(66 . r,   c                 F   U[        U5      :  a  g[        SU[        -
  5      n[        [        U5      U[        -   5      n[	        X45       HS  nU R                  X   5      (       d  M  U R                  [        SX   5      5      nU R                  XX&5      (       d  MS    g   g)z?Check if cutting at index would separate AI/Tool message pairs.Tr   r   F)	rM   max_SEARCH_RANGE_FOR_TOOL_PAIRSminrN   _has_tool_calls_extract_tool_call_idsr   _cutoff_separates_tool_pair)r(   r0   r8   search_start
search_endrQ   tool_call_idss          r*   rO   -SummarizationMiddleware._is_safe_cutoff_point   s    3x=(1l-IIJX7S(ST
|0A''44 77[(+8VWM//\YY 1 r,   messagec                 n    [        U[        5      =(       a    [        US5      =(       a    UR                  $ )z2Check if message is an AI message with tool calls.
tool_calls)r&   r   hasattrr`   )r(   r^   s     r*   rW   'SummarizationMiddleware._has_tool_calls   s+     w	*dww/MdRYRdRd	
r,   
ai_messagec                     [        5       nUR                   HL  n[        U[        5      (       a  UR	                  S5      O[        USS5      nUc  M;  UR                  U5        MN     U$ )z)Extract tool call IDs from an AI message.r1   N)setr`   r&   dictgetgetattradd)r(   rc   r\   tccall_ids        r*   rX   .SummarizationMiddleware._extract_tool_call_ids   sZ    ''B&0T&:&:bffTlDRV@WG"!!'* ( r,   ai_message_indexr\   c                     [        US-   [        U5      5       H?  nX   n[        U[        5      (       d  M  UR                  U;   d  M0  X#:  nXS:  nXx:w  d  M?    g   g)zMCheck if cutoff separates an AI message from its corresponding tool messages.   TF)rN   rM   r&   r   tool_call_id)	r(   r0   rm   r8   r\   jr^   ai_before_cutofftool_before_cutoffs	            r*   rY   3SummarizationMiddleware._cutoff_separates_tool_pair   s`     '!+S];AkG';//G4H4HM4Y#3#B %&%5"#9 < r,   r9   c                 ,   U(       d  gU R                  U5      nU(       d  g U R                  R                  U R                  R	                  US95      n[        SUR                  5      R                  5       $ ! [         a  nSU< 3s SnA$ SnAff = f)z(Generate summary for the given messages.z!No previous conversation history.z0Previous conversation was too long to summarize.)r0   r'   zError generating summary: N)	_trim_messages_for_summaryr   invoker    formatr   r@   strip	Exception)r(   r9   trimmed_messagesresponsees        r*   r5   'SummarizationMiddleware._create_summary   s    $6::;PQE	6zz(()<)<)C)CM])C)^_Hx//06688 	6/u55	6s   AA9 9
BBBBc           
      r     [        U[        U R                  SSSSS9$ ! [         a    U[        * S s $ f = f)z6Trim messages to fit within summary generation limits.humanlastT)
max_tokensr   start_onstrategyallow_partialinclude_systemN)r   _DEFAULT_TRIM_TOKEN_LIMITr   rz   _DEFAULT_FALLBACK_MESSAGE_COUNT)r(   r0   s     r*   rv   2SummarizationMiddleware._trim_messages_for_summary   sR    	? 4"00 "#   	?<<=>>	?s    66)r   r   r   r!   r    r   )&__name__
__module____qualname____firstlineno____doc___DEFAULT_MESSAGES_TO_KEEPr   DEFAULT_SUMMARY_PROMPTSUMMARY_PREFIXr'   r   intTokenCounterr%   r   r   rf   r   r=   listr   r6   r   r2   tupler4   r3   boolrO   rW   r   re   rX   rY   r5   rv   __static_attributes____classcell__)r)   s   @r*   r   r   >   s    15 9&@4,-]"- $':- 	-
 $- - - 
- -@
* 
w 
4S>TXCX 
<
3 
43E 

+D,< + +	9#J/	9 	9 
tJj!11	2		9$z*: s "d:.> c VZ $
z 
d 
 s3x z"  	
 3x 
"6T*5E 6# 6?4
3C ?ZHX ? ?r,   r   )&r   rC   collections.abcr   r   typingr   r   langchain_core.messagesr   r   r	   r
   r   langchain_core.messages.humanr   langchain_core.messages.utilsr   r   langgraph.graph.messager   langgraph.runtimer   !langchain.agents.middleware.typesr   r   langchain.chat_modelsr   r   r   r   r   r   r   r   r   rU   r   rI   r,   r*   <module>r      s      .   7 S & I @";<=sBC 8 5   "$   {?o {?r,   