+
    ӃiTv                         R t ^ RIt^ RIt^ RIHtHtHtHt ^ RIH	t	 ^ RI
HtHt ]P                  ! ]4      tRtRtRtRtR	tR
t^tRt ! R R4      tR# )a  Automatic context window compression for long conversations.

Self-contained class with its own OpenAI client for summarization.
Uses auxiliary model (cheap/fast) to summarize middle turns while
protecting head and tail context.

Improvements over v1:
  - Structured summary template (Goal, Progress, Decisions, Files, Next Steps)
  - Iterative summary updates (preserves info across multiple compactions)
  - Token-budget tail protection instead of fixed message count
  - Tool output pruning before LLM summarization (cheap pre-pass)
  - Scaled summary budget (proportional to compressed content)
  - Richer tool call/result detail in summarizer input
N)AnyDictListOptional)call_llm)get_model_context_lengthestimate_messages_tokens_roughai  [CONTEXT COMPACTION] Earlier turns in this conversation were compacted to save context space. The summary below describes work that was already completed, and the current session state may still reflect that work (for example, files may already be changed). Use the summary and the current state to continue from where things left off, and avoid repeating work:z[CONTEXT SUMMARY]:i  皙?i.  z/[Old tool output cleared to save context space]iX  c                   h  a  ] tR t^5t o RtR$V 3R lR lltV 3R lR ltR%V 3R lR lltV 3R	 lR
 ltV 3R lR lt	V 3R lR lt
V 3R lR ltV 3R lR ltV 3R lR lt]V 3R lR l4       t]V 3R lR l4       tV 3R lR ltV 3R lR ltV 3R lR ltR%V 3R lR  lltR%V 3R! lR" lltR#tV tR# )&ContextCompressora  Compresses conversation context when approaching the model's context limit.

Algorithm:
  1. Prune old tool results (cheap, no LLM call)
  2. Protect head messages (system prompt + first exchange)
  3. Protect tail messages by token budget (most recent ~20K tokens)
  4. Summarize middle turns with structured LLM prompt
  5. On subsequent compactions, iteratively update the previous summary
Nc                j   < V ^8  d   QhRS[ RS[RS[RS[RS[RS[RS[ RS[ R	S[ R
S[R,          RS[ /# )   modelthreshold_percentprotect_first_nprotect_last_nsummary_target_ratio
quiet_modesummary_model_overridebase_urlapi_keyconfig_context_lengthNprovider)strfloatintbool)format__classdict__s   "5/home/ubuntu/hermes-agent/agent/context_compressor.py__annotate__ContextCompressor.__annotate__@   s}     ;: ;:;: !;: 	;:
 ;: $;: ;: !$;: ;: ;:  #Tz;: ;:    c                   Wn         Wn        Wn        Wn        W n        W0n        W@n        \        R \        VR4      4      V n	        W`n
        \        WV	V
VR7      V n        \        V P                  V,          4      V n        ^ V n        \        V P                  V P                  ,          4      pWn        \        \        V P                  R,          4      \"        4      V n        V'       gg   \&        P)                  RYP                  V P                  V^d,          V P                  ^d,          V P                   T;'       g    RT;'       g    R4	       RV n        ^ V n        ^ V n        ^ V n        T;'       g    RV n        RV n        R	V n        R# )
g?g?)r   r   r   r   g?zContext compressor initialized: model=%s context_length=%d threshold=%d (%.0f%%) target_ratio=%.0f%% tail_budget=%d provider=%s base_url=%snoneF N        )r   r   r   r   r   r   r   maxminr   r   r   context_lengthr   threshold_tokenscompression_counttail_token_budget_SUMMARY_TOKENS_CEILINGmax_summary_tokensloggerinfo_context_probedlast_prompt_tokenslast_completion_tokenslast_total_tokenssummary_model_previous_summary_summary_failure_cooldown_until)selfr   r   r   r   r   r   r   r   r   r   r   target_tokenss   &&&&&&&&&&&& r   __init__ContextCompressor.__init__@   sg    
  !2.,$'c2F.M$N!$6g"7

 !$D$7$7:K$K L!" D11D4M4MMN!."%##d*+-D#
 KK* **D,A,A!C')B)BS)H&&""FH$6$6  %"#&'#!"399r 1569,r"   c                6   < V ^8  d   QhRS[ S[S[3,          /# )r   usager   r   r   )r   r   s   "r   r    r!   }   s     > >$sCx. >r"   c                    VP                  R^ 4      V n        VP                  R^ 4      V n        VP                  R^ 4      V n        R# )z-Update tracked token usage from API response.prompt_tokenscompletion_tokenstotal_tokensN)getr2   r3   r4   )r8   r=   s   &&r   update_from_response&ContextCompressor.update_from_response}   s<    "'))OQ"?&+ii0CQ&G#!&>1!=r"   c                &   < V ^8  d   QhRS[ RS[/# )r   r@   return)r   r   )r   r   s   "r   r    r!      s     / /S /D /r"   c                B    Ve   TMV P                   pW P                  8  # )z3Check if context exceeds the compression threshold.)r2   r*   )r8   r@   tokenss   && r   should_compress!ContextCompressor.should_compress   s#    "/";AXAX....r"   c                L   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[/# r   messagesrG   )r   r   r   r   r   )r   r   s   "r   r    r!      s(     7 7$tCH~2F 74 7r"   c                4    \        V4      pW P                  8  # )z>Quick pre-flight check using rough estimate (before API call).)r   r*   )r8   rN   rough_estimates   && r   should_compress_preflight+ContextCompressor.should_compress_preflight   s    7A!6!666r"   c                6   < V ^8  d   QhRS[ S[S[3,          /# r   rG   r>   )r   r   s   "r   r    r!      s     
 
DcN 
r"   c                    RV P                   RV P                  RV P                  RV P                  '       d/   \        ^dV P                   V P                  ,          ^d,          4      M^ RV P                  /# )z3Get current compression status for display/logging.r2   r*   r)   usage_percentr+   )r2   r*   r)   r(   r+   )r8   s   &r   
get_statusContextCompressor.get_status   sl     !$"9"9 5 5d11`d`s`s`sSt'>'>ATAT'TWZ'Z\yz!7!7
 	
r"   c          
         < V ^8  d   QhRS[ S[S[S[3,          ,          RS[RS[S[ S[S[S[3,          ,          S[3,          /# )r   rN   protect_tail_countrG   )r   r   r   r   r   tuple)r   r   s   "r   r    r!      sI      T#s(^,BE	tDcN#S(	)r"   c                   V'       g   V^ 3# V Uu. uF  q3P                  4       NK  	  pp^ p\        V4      V,
          p\        V4       Fs  pWG,          pVP                  R4      R8w  d   K#  VP                  RR4      p	V	'       d   V	\        8X  d   KJ  \        V	4      ^8  g   K\  / VCR\        /CWG&   V^,          pKu  	  WE3# u upi )a  Replace old tool result contents with a short placeholder.

Walks backward from the end, protecting the most recent
``protect_tail_count`` messages. Older tool results get their
content replaced with a placeholder string.

Returns (pruned_messages, pruned_count).
roletoolcontentr%   )copylenrangerC   _PRUNED_TOOL_PLACEHOLDER)
r8   rN   rZ   mresultprunedprune_boundaryimsgr_   s
   &&&       r   _prune_old_tool_results)ContextCompressor._prune_old_tool_results   s     Q;$,-Hq&&(H-V'99~&A)Cwwv&(ggi,Gg)AA7|c!HsHI/GH	! ' ~! .s   Cc                L   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[/# r   turns_to_summarizerG   r   r   r   r   r   )r   r   s   "r   r    r!      s-     	N 	N$tCH~:N 	NSV 	Nr"   c                    \        V4      p\        V\        ,          4      p\        \        \        W0P                  4      4      # )a  Scale summary token budget with the amount of content being compressed.

The maximum scales with the model's context window (5% of context,
capped at ``_SUMMARY_TOKENS_CEILING``) so large-context models get
richer summaries instead of being hard-capped at 8K tokens.
)r   r   _SUMMARY_RATIOr'   _MIN_SUMMARY_TOKENSr(   r.   )r8   rn   content_tokensbudgets   &&  r   _compute_summary_budget)ContextCompressor._compute_summary_budget   s7     88JK^n45&F4K4K(LMMr"   c                L   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[/# )r   turnsrG   r   r   r   r   )r   r   s   "r   r    r!      s(     1" 1"Dc3h,@ 1"S 1"r"   c           	        . pV EF/  pVP                  RR4      pVP                  R4      ;'       g    RpVR8X  dV   VP                  RR4      p\        V4      R8  d   VR,          R	,           VR R
 ,           pVP                  RV RV 24       K  VR8X  EdL   \        V4      R8  d   VR,          R	,           VR R
 ,           pVP                  R. 4      pV'       d   . pV F  p	\        V	\        4      '       dq   V	P                  R/ 4      p
V
P                  RR4      pV
P                  RR4      p\        V4      R8  d   VR,          R,           pVP                  RV RV R24       K  \        V	RR
4      p
V
'       d   \        V
RR4      MRpVP                  RV R24       K  	  VRRP                  V4      ,           R,           ,          pVP                  RV 24       EK  \        V4      R8  d   VR,          R	,           VR R
 ,           pVP                  RVP                  4        RV 24       EK2  	  RP                  V4      # )!zSerialize conversation turns into labeled text for the summarizer.

Includes tool call arguments and result content (up to 3000 chars
per message) so the summarizer can preserve specific details like
file paths, commands, and outputs.
r]   unknownr_   r%   r^   tool_call_idi  :Ni  Nz
...[truncated]...
Nz[TOOL RESULT z]: 	assistant
tool_callsfunctionname?	argumentsi  :Ni  Nz...z  ()z(...)z
[Tool calls:

z
]z[ASSISTANT]: [

i)rC   ra   append
isinstancedictgetattrjoinupper)r8   rx   partsri   r]   r_   tool_idr~   tc_partstcfnr   argss   &&           r   _serialize_for_summary(ContextCompressor._serialize_for_summary   s     C7769-Dggi(..BG v~''."5w<$&%en/FFQUQVWG}WIS	BC {"w<$&%en/FFQUQVWG WW\26
!H(%b$//!#
B!7B#%66&##6D#%66+r#:D"4y3'+DzE'9$OObavQ,?@!(Z!>B?A72vs#;sD$OObe,<= ) 1DIIh4GG%OOG}WI67 7|d"!%.+BBWTU^SLL1TZZ\N#gY78O R {{5!!r"   c                \   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[S[,          /# rm   )r   r   r   r   r   )r   r   s   "r   r    r!      s1     H HDc3h4H HXVY] Hr"   c                   \         P                  ! 4       pW P                  8  d*   \        P	                  RV P                  V,
          4       R# V P                  V4      pV P                  V4      pV P                  '       d   RV P                   RV RV R2pM	RV RV R	2p R
RRRRRV/.RV^,          /pV P                  '       d   V P                  VR&   \        R/ VB pVP                  ^ ,          P                  P                  p\        V\        4      '       g   V'       d   \        V4      MRpVP                  4       p	Wn        RV n        V P!                  V	4      #   \"         dE    \         P                  ! 4       \$        ,           T n        \&        P(                  ! R\$        4        R# \*         dM   p
\         P                  ! 4       \$        ,           T n        \&        P(                  ! RT
\$        4        Rp
?
R# Rp
?
ii ; i)u  Generate a structured summary of conversation turns.

Uses a structured template (Goal, Progress, Decisions, Files, Next Steps)
inspired by Pi-mono and OpenCode. When a previous summary exists,
generates an iterative update instead of summarizing from scratch.

Returns None if all attempts fail — the caller should drop
the middle turns without a summary rather than inject a useless
placeholder.
z:Skipping context summary during cooldown (%.0fs remaining)NzYou are updating a context compaction summary. A previous compaction produced the summary below. New conversation turns have occurred since then and need to be incorporated.

PREVIOUS SUMMARY:
z

NEW TURNS TO INCORPORATE:
u)  

Update the summary using this exact structure. PRESERVE all existing information that is still relevant. ADD new progress. Move items from "In Progress" to "Done" when completed. Remove information only if it is clearly obsolete.

## Goal
[What the user is trying to accomplish — preserve from previous summary, update if goal evolved]

## Constraints & Preferences
[User preferences, coding style, constraints, important decisions — accumulate across compactions]

## Progress
### Done
[Completed work — include specific file paths, commands run, results obtained]
### In Progress
[Work currently underway]
### Blocked
[Any blockers or issues encountered]

## Key Decisions
[Important technical decisions and why they were made]

## Relevant Files
[Files read, modified, or created — with brief note on each. Accumulate across compactions.]

## Next Steps
[What needs to happen next to continue the work]

## Critical Context
[Any specific values, error messages, configuration details, or data that would be lost without explicit preservation]

Target ~u    tokens. Be specific — include file paths, command outputs, error messages, and concrete values rather than vague descriptions.

Write only the summary body. Do not include any preamble or prefix.zCreate a structured handoff summary for a later assistant that will continue this conversation after earlier turns are compacted.

TURNS TO SUMMARIZE:
u  

Use this exact structure:

## Goal
[What the user is trying to accomplish]

## Constraints & Preferences
[User preferences, coding style, constraints, important decisions]

## Progress
### Done
[Completed work — include specific file paths, commands run, results obtained]
### In Progress
[Work currently underway]
### Blocked
[Any blockers or issues encountered]

## Key Decisions
[Important technical decisions and why they were made]

## Relevant Files
[Files read, modified, or created — with brief note on each]

## Next Steps
[What needs to happen next to continue the work]

## Critical Context
[Any specific values, error messages, configuration details, or data that would be lost without explicit preservation]

Target ~u!   tokens. Be specific — include file paths, command outputs, error messages, and concrete values rather than vague descriptions. The goal is to prevent the next assistant from repeating work or losing important details.

Write only the summary body. Do not include any preamble or prefix.taskcompressionrN   r]   userr_   
max_tokensr   r%   r&   ztContext compression: no provider available for summary. Middle turns will be dropped without summary for %d seconds.zWFailed to generate context summary: %s. Further summary attempts paused for %d seconds. )time	monotonicr7   r/   debugru   r   r6   r5   r   choicesmessager_   r   r   strip_with_summary_prefixRuntimeError!_SUMMARY_FAILURE_COOLDOWN_SECONDSloggingwarning	Exception)r8   rn   nowsummary_budgetcontent_to_summarizepromptcall_kwargsresponser_   summaryes   &&         r   _generate_summary#ContextCompressor._generate_summary   s    nn555LLL44s: 556HI#::;MN!!!      	< 
 DI&GFR   	< 
 DC#GFJ"	ffi@Anq0K !!!'+'9'9G$.+.H&&q)1199Ggs++*1#g,rmmoG%,"36D0,,W55 	37>>3CFg3gD0OO . >?  	37>>3CFg3gD0OOB1	 	s3   $%E$ 
AE$ ):E$ $AH2H;H<AHHc                &   < V ^8  d   QhRS[ RS[ /# )r   r   rG   r   )r   r   s   "r   r    r!     s     G Gc Gc Gr"   c                    T ;'       g    RP                  4       p\        \        3 F8  pVP                  V4      '       g   K  V\	        V4      R P                  4       p M	  V'       d   \         RV 2# \        # )z@Normalize summary text to the current compaction handoff format.r%   Nr   )r   LEGACY_SUMMARY_PREFIXSUMMARY_PREFIX
startswithra   lstrip)r   textprefixs   &  r   r   &ContextCompressor._with_summary_prefix  sm     2$$&,n=Fv&&CKL)002 > /3.!D6*FFr"   c                    < V ^8  d   QhRS[ /# rT   r   )r   r   s   "r   r    r!     s     + + +r"   c                    \        V \        4      '       d   V P                  RR4      # \        V RR4      ;'       g    R# )zEExtract the call ID from a tool_call entry (dict or SimpleNamespace).idr%   )r   r   rC   r   )r   s   &r   _get_tool_call_id#ContextCompressor._get_tool_call_id  s9     b$66$##r4$***r"   c                r   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[ S[S[S[3,          ,          /# rM   ry   )r   r   s   "r   r    r!     s8     : :T$sCx.-A :d4PSUXPX>FZ :r"   c           
     B   \        4       pV Fi  pVP                  R4      R8X  g   K  VP                  R4      ;'       g    .  F/  pV P                  V4      pV'       g   K  VP                  V4       K1  	  Kk  	  \        4       pV FG  pVP                  R4      R8X  g   K  VP                  R4      pV'       g   K6  VP                  V4       KI  	  Wb,
          pV'       dq   V Uu. uF3  pVP                  R4      R8X  d   VP                  R4      V9   d   K1  VNK5  	  ppV P                  '       g    \
        P                  R\        V4      4       W&,
          p	V	'       d   . p
V F~  pV
P                  V4       VP                  R4      R8X  g   K,  VP                  R4      ;'       g    .  F3  pV P                  V4      pWY9   g   K  V
P                  RRRRRV/4       K5  	  K  	  T
pV P                  '       g    \
        P                  R	\        V	4      4       V# u upi )
aI  Fix orphaned tool_call / tool_result pairs after compression.

Two failure modes:
1. A tool *result* references a call_id whose assistant tool_call was
   removed (summarized/truncated).  The API rejects this with
   "No tool call found for function call output with call_id ...".
2. An assistant message has tool_calls whose results were dropped.
   The API rejects this because every tool_call must be followed by
   a tool result with the matching call_id.

This method removes orphaned results and inserts stub results for
orphaned calls so the message list is always well-formed.
r]   r}   r~   r^   r|   z9Compression sanitizer: removed %d orphaned tool result(s)r_   u@   [Result from earlier conversation — see context summary above]z3Compression sanitizer: added %d stub tool result(s))	setrC   r   addr   r/   r0   ra   r   )r8   rN   surviving_call_idsri   r   cidresult_call_idsorphaned_resultsrd   missing_resultspatcheds   &&         r   _sanitize_tool_pairs&ContextCompressor._sanitize_tool_pairs  s    #&%Cwwv+-'',/5525B004Cs*..s3 6   #uCwwv&(ggn-3#'',	  +?##af/AEE.4IM]4] 8   ???WY\]mYno ->,.Gs#776?k1!ggl399r9"44R81#NN & )+m .,  :   H???QSVWfSgh5s   %.HHc                R   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[RS[/# r   rN   idxrG   ro   )r   r   s   "r   r    r!     s0      T#s(^0D 3 SV r"   c                t    V\        V4      8  d(   W,          P                  R4      R8X  d   V^,          pK7  V# )zPush a compress-start boundary forward past any orphan tool results.

If ``messages[idx]`` is a tool result, slide forward until we hit a
non-tool message so we don't start the summarised region mid-group.
r]   r^   ra   rC   )r8   rN   r   s   &&&r   _align_boundary_forward)ContextCompressor._align_boundary_forward  s2     CM!hm&7&7&?6&I1HC
r"   c                R   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[RS[/# r   ro   )r   r   s   "r   r    r!     s0      d38n1E C TW r"   c                *   V^ 8:  g   V\        V4      8  d   V# V^,
          pV^ 8  d(   W,          P                  R4      R8X  d   V^,          pK.  V^ 8  d<   W,          P                  R4      R8X  d    W,          P                  R4      '       d   TpV# )a  Pull a compress-end boundary backward to avoid splitting a
tool_call / result group.

If the boundary falls in the middle of a tool-result group (i.e.
there are consecutive tool messages before ``idx``), walk backward
past all of them to find the parent assistant message.  If found,
move the boundary before the assistant so the entire
assistant + tool_results group is included in the summarised region
rather than being split (which causes silent data loss when
``_sanitize_tool_pairs`` removes the orphaned tail results).
r]   r^   r}   r~   r   )r8   rN   r   checks   &&& r   _align_boundary_backward*ContextCompressor._align_boundary_backward  s     !8sc(m+JaqjX_008FBQJE A:(/--f5DI\I\]iIjIjC
r"   c                f   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[RS[R,          RS[/# )r   rN   head_endtoken_budgetNrG   ro   )r   r   s   "r   r    r!     s@     1* 1*T#s(^,1*8;1*Dj1* 
1*r"   c                   Vf   V P                   p\        V4      pV P                  p^ pTp\        V^,
          V^,
          R4       F  pW,          p	V	P	                  R4      ;'       g    Rp
\        V
4      \
        ,          ^
,           pV	P	                  R4      ;'       g    .  FZ  p\        V\        4      '       g   K  VP	                  R/ 4      P	                  RR4      pV\        V4      \
        ,          ,          pK\  	  Wk,           V8  d   WH,
          V8  d    MWk,          pTpK  	  WE,
          pW~8  d   TpWr8:  d   TpV P                  W4      p\        Wr^,           4      # )a  Walk backward from the end of messages, accumulating tokens until
the budget is reached. Returns the index where the tail starts.

``token_budget`` defaults to ``self.tail_token_budget`` which is
derived from ``summary_target_ratio * context_length``, so it
scales automatically with the model's context window.

Never cuts inside a tool_call/result group. Falls back to the old
``protect_last_n`` if the budget would protect fewer messages.
r_   r%   r~   r   r   )
r,   ra   r   rb   rC   _CHARS_PER_TOKENr   r   r   r'   )r8   rN   r   r   nmin_tailaccumulatedcut_idxrh   ri   r_   
msg_tokensr   r   fallback_cuts   &&&&           r   _find_tail_cut_by_tokens*ContextCompressor._find_tail_cut_by_tokens  sC    11LM&&q1uhlB/A+Cggi(..BGW)99B>Jggl+11r1b$''66*b155k2FD#d)/?"??J 2 ',6AEh;N%KG 0 |!"G
 "G //B7qL))r"   c          	      x   < V ^8  d   QhRS[ S[S[S[3,          ,          RS[RS[ S[S[S[3,          ,          /# )r   rN   current_tokensrG   ro   )r   r   s   "r   r    r!   5  sG     C Cd38n!5 Cs CVZ[_`ceh`h[iVj Cr"   c                   \        V4      pW0P                  V P                  ,           ^,           8:  dN   V P                  '       g:   \        P                  RVV P                  V P                  ,           ^,           4       V# V'       d   TMV P                  ;'       g    \        V4      pV P                  WP                  ^,          R7      w  rV'       d)   V P                  '       g   \        P                  RV4       V P                  pV P                  W4      pV P                  W4      pWg8  d   V# WV pV P                  '       g   \        P                  RVV P                  4       \        P                  RV P                  V P                  ^d,          V P                  4       W7,
          p	\        P                  RV^,           V\        V4      VV	4       V P                  V4      p
. p\!        V4       F}  pW,          P#                  4       pV^ 8X  dM   VP%                  R4      R8X  d7   V P&                  ^ 8X  d&   VP%                  R	4      ;'       g    R
R,           VR	&   VP)                  V4       K  	  RpV
'       d   V^ 8  d    W^,
          ,          P%                  RR4      MRpWs8  d   W,          P%                  RR4      MRpVR9   d   RpMRpVV8X  d   VR8X  d   RMRpVV8w  d   TpMRpV'       g   VP)                  RVR	V
/4       M'V P                  '       g   \        P+                  R4       \!        Ws4       Fh  pW,          P#                  4       pV'       d7   W8X  d1   VP%                  R	4      ;'       g    R
pV
R,           V,           VR	&   RpVP)                  V4       Kj  	  V ;P&                  ^,          un        V P-                  V4      pV P                  '       gV   \        V4      pVV,
          p\        P                  RV\        V4      V4       \        P                  RV P&                  4       V# )a  Compress conversation messages by summarizing middle turns.

Algorithm:
  1. Prune old tool results (cheap pre-pass, no LLM call)
  2. Protect head messages (system prompt + first exchange)
  3. Find tail boundary by token budget (~20K tokens of recent context)
  4. Summarize middle turns with structured LLM prompt
  5. On re-compression, iteratively update the previous summary

After compression, orphaned tool_call / tool_result pairs are cleaned
up so the API never receives mismatched IDs.
z-Cannot compress: only %d messages (need > %d))rZ   z-Pre-compression: pruned %d old tool result(s)z9Context compression triggered (%d tokens >= %d threshold)z,Model context limit: %d tokens (%.0f%% = %d)zISummarizing turns %d-%d (%d turns), protecting %d head + %d tail messagesr]   systemr_   r%   z

[Note: Some earlier conversation turns have been compacted into a handoff summary to preserve context space. The current session state may still reflect earlier work, so build on that summary and state rather than re-doing work.]Fr   r}   TuC   No summary model available — middle turns dropped without summaryr   z0Compressed: %d -> %d messages (~%d tokens saved)zCompression #%d complete)r}   r^   )ra   r   r   r   r/   r   r2   r   rj   r0   r   r   r*   r)   r   r   rb   r`   rC   r+   r   r   r   )r8   rN   r   
n_messagesdisplay_tokenspruned_countcompress_startcompress_endrn   	tail_msgsr   
compressedrh   ri   _merge_summary_into_taillast_head_rolefirst_tail_rolesummary_roleflippedoriginalnew_estimatesaved_estimates   &&&                   r   compressContextCompressor.compress5  s    ]
--0C0CCaGG???C((4+>+>>B
 O+9t?V?V  @C  @CZx  zB  [C "&!=!=)<)<q)@ "> "
 KKGV --55hO 44XN)O%\BKKK%%
 KK>##&&,%%	 #1IKK["&' (();< 
~&A+""$CAv#''&/X5$:P:PTU:UWWY'--2 BB I c" ' $) Q_bcQcXq&89==ffMioNLXLeh488HkqO !66%* .)5)?+Vn,#*L 04,+!!6<G"LM???bc|0A+""$C'A,=779-33!(6!1H!<I+0(c" 1 	!#..z:
9*EL+l:NKKBJ	 KK2D4J4JKr"   )r1   r6   r7   r   r   r+   r)   r3   r2   r4   r.   r   r   r   r   r   r5   r   r,   r   r*   )
g      ?      r	   FNr%   r%   Nr%   )N)__name__
__module____qualname____firstlineno____doc__r:   rD   rJ   rQ   rW   rj   ru   r   r   staticmethodr   r   r   r   r   r   r   __static_attributes____classdictcell__)r   s   @r   r   r   5   s     ;: ;:z> >/ /
7 7

 
 H	N 	N1" 1"fH HT G G + +: :x  81* 1*nC C Cr"   r   )r  r   r   typingr   r   r   r   agent.auxiliary_clientr   agent.model_metadatar   r   	getLoggerr   r/   r   r   rr   rq   r-   rc   r   r   r   r   r"   r   <module>r     sz      , , +
 
		8	$  -      M   $' !C
 C
r"   