
    PL
ja                        d Z ddlmZ ddlZddlZddlZddlZddlmZ ddlm	Z	 ddl
mZmZmZmZ ddlmZ  ej        e          ZddZddZddddd dZd!dZg dZdS )"u  Context compression — extract the AIAgent methods that drive summarisation.

Three concerns live here:

* :func:`check_compression_model_feasibility` — startup probe of the
  configured auxiliary compression model.  Warns when the aux context
  window can't fit the main model's compression threshold; auto-lowers
  the session threshold when possible; hard-rejects auxes below
  ``MINIMUM_CONTEXT_LENGTH``.

* :func:`replay_compression_warning` — re-emit a stored warning through
  the gateway ``status_callback`` once it's wired up (the callback is
  set after :class:`AIAgent` construction).

* :func:`compress_context` — the actual compression call.  Runs the
  configured compressor, splits the SQLite session, rotates the
  session_id, notifies plugin context engines / memory providers, and
  returns the compressed message list and freshly-built system prompt.

* :func:`try_shrink_image_parts_in_messages` — image-too-large recovery
  helper that re-encodes ``data:image/...;base64,...`` parts at a smaller
  size so retries can fit under provider ceilings (Anthropic's 5 MB).

``run_agent`` keeps thin wrappers for each so existing call sites
(``self._compress_context(...)``) keep working.  Tests that exercise
these paths see no behavioural change.
    )annotationsN)datetime)Path)AnyListOptionalTuple)estimate_request_tokens_roughagentr   returnNonec                p   | j         sdS 	 ddlm}m} ddlm}m}  |d|                                           \  }}	  |d          \  }}}}}n# t          $ r d}Y nw xY w||sI|r|dk    rd	| d
}	nd}	|	| _	        | 
                    |	           t                              d           dS t          t          |dd                    }
t          t          |dd                    } |||
|t          | dd          |r|dk    r|nt          | dd          | j                  }|r'||k     r!t!          d| d|dd|dd|dz   d	          | j        j        }||k     r|}|}|| j        _        | j        j        }|r||z  | j        _        |rt+          ||z  dz            nd}t          | dd          pd}t          | dd          pd}|r|dk    r|nd}|s.	 ddlm}  ||
          j        p|
}n# t          $ r |
pd}Y nw xY w|r| d| dn|}| d| d}d | d!|dd"| d#|dd$|dd%|dd&|d'}	|	| _	        | 
                    |	           t                              d(||||           dS dS # t           $ r  t          $ r&}t                              d)|           Y d}~dS d}~ww xY w)*au  Warn at session start if the auxiliary compression model's context
    window is smaller than the main model's compression threshold.

    When the auxiliary model cannot fit the content that needs summarising,
    compression will either fail outright (the LLM call errors) or produce
    a severely truncated summary.

    Called during ``AIAgent.__init__`` so CLI users see the warning
    immediately (via ``_vprint``).  The gateway sets ``status_callback``
    *after* construction, so :func:`replay_compression_warning` re-sends
    the stored warning through the callback on the first
    ``run_conversation()`` call.
    Nr   )_resolve_task_provider_modelget_text_auxiliary_client)MINIMUM_CONTEXT_LENGTHget_model_context_lengthcompression)main_runtime autou/   ⚠ Configured auxiliary compression provider 'u   ' is unavailable — context compression will drop middle turns without a summary. Check auxiliary.compression in config.yaml and reauthenticate that provider.u   ⚠ No auxiliary LLM provider configured — context compression will drop middle turns without a summary. Run `hermes setup` or set OPENROUTER_API_KEY.uL   No auxiliary LLM provider for compression — summaries will be unavailable.base_urlapi_key&_aux_compression_context_length_configprovider)r   r   config_context_lengthr   custom_providerszAuxiliary compression model z has a context window of ,z$ tokens, which is below the minimum zE required by Hermes Agent.  Choose a compression model with at least i  zK context (set auxiliary.compression.model in config.yaml), or set auxiliary.compression.context_length to override the detected value if it is wrong.d   2   model?)urlparsez ()u   ⚠ Compression model z context is z tokens, but the main model z's compression threshold was z2 tokens. Auto-lowered this session's threshold to u    tokens so compression can run.
  To make this permanent, edit config.yaml — either:
  1. Use a larger compression model:
       auxiliary:
         compression:
           model: <model-with-z[+-context>
  2. Lower the compression threshold:
       compression:
         threshold: 0.02du   Auxiliary compression model %s has %d token context, below the main model's compression threshold of %d tokens — auto-lowered session threshold to %d to keep compression working.z4Compression feasibility check failed (non-fatal): %s)compression_enabledagent.auxiliary_clientr   r   agent.model_metadatar   r   _current_main_runtime	Exception_compression_warning_emit_statusloggerwarningstrgetattr_custom_providers
ValueErrorcontext_compressorthreshold_tokenscontext_lengththreshold_percentinturllib.parser"   hostnamedebug)r   r   r   r   r   client	aux_model_aux_cfg_provider_msgaux_base_urlaux_api_keyaux_context	thresholdold_thresholdnew_thresholdmain_ctxsafe_pct_main_model_main_provider_aux_provider_labelr"   _main_label
_aux_labelexcs                            B/home/kuhnn/.hermes/hermes-agent/agent/conversation_compression.py#check_compression_model_feasibilityrN   ,   sB    $ b
	
 	
 	
 	
 	
 	
 	
 	
	
 	
 	
 	
 	
 	
 	
 	

 654466
 
 
		#,H,H,W,W)q!Q 	# 	# 	# "	#>>  %6&%@%@4)4 4 4 D 
 *-E&s###NN1   F76:r::;;'&)R8899..!")%1Y[_"`"` ,=  AARV\A\A\''bijoq{}  cA  cA"4

 

 

$  
	;)???2y 2 2(H2 21I2 2 *T1	2 2 2	 	 	 ,=	"" &M'M8EE$5 />H !H, (: ?GNsK(2c9:::BH "%"55<K$UJ;;ArN %):f)D)D "!  
 ' AA555555 ..7G< (' ! A A A*6*@&'''A "!;33.3333  
 &??)<???J8 8 8>8 88 8 !-8 8
 !E8 8 2?N8 8 *278 8   *-E&s###NN, 	 	 	 	 	E #"X     	 
 
 
BC	
 	
 	
 	
 	
 	
 	
 	
 	

sg   2I; A I; AI; AAI; /D0I;  G9 8I; 9H
I; 	H

A-I; ;J5J00J5c                    t          | dd          }|r1| j        r,	 |                     d|           dS # t          $ r Y dS w xY wdS dS )u  Re-send the compression warning through ``status_callback``.

    During ``__init__`` the gateway's ``status_callback`` is not yet
    wired, so ``_emit_status`` only reaches ``_vprint`` (CLI).  This
    method is called once at the start of the first
    ``run_conversation()`` — by then the gateway has set the callback,
    so every platform (Telegram, Discord, Slack, etc.) receives the
    warning.
    r*   N	lifecycle)r/   status_callbackr)   )r   r>   s     rM   replay_compression_warningrR      s     %/
6
6C
 u$ 	!!+s33333 	 	 	DD	   s   4 
AAdefault)approx_tokenstask_idfocus_topicmessageslistsystem_messager.   rT   Optional[int]rU   rV   Optional[str]Tuple[list, str]c                  t          |          }t                              d| j        pd||r|dnd| j        |           |                     d           | j        r,	 | j                            |           n# t          $ r Y nw xY w	 | j	        
                    |||          }n,# t          $ r | j	        
                    ||          }Y nw xY wt          | j	        dd	          }|r6t          | d
d	          |k    r || _        |                     d| d           nlt          | j	        dd	          }	t          | j	        dd	          }
|	r>|	|
f}t          | dd	          |k    r%|| _        |                     d|	 d|
pd d           | j                                        }|r|                    d|d           |                                  |                     |          }|| _        | j        r:	 | j                            | j                  }|                     |           | j                            | j        d           | j        }t5          j                                        d           dt;          j                    j        d	d          | _        | j        t@          j!        d<   	 ddl"m#} |$                    | j                   n# t          $ r Y nw xY w| j%        d| j         dz  | _&        d| _'        | j        (                    | j        | j)        pt@          j!        *                    d d!          | j        | j+        |"           d#| _'        |ru	 | j        ,                    |          }| j        -                    | j        |           n9# t\          t          f$ r%}t          /                    d$|           Y d	}~nd	}~ww xY w| j        0                    | j        |           d| _1        n2# t          $ r%}t          2                    d%|           Y d	}~nd	}~ww xY w	 tg                      *                    d&          }|r9ti          | j	        d'          r$| j	        5                    | j        pd(d|)           n2# t          $ r%}t          /                    d*|           Y d	}~nd	}~ww xY w	 tg                      *                    d&          }|r,| j        r%| j        6                    | j        pd(|dd+           n2# t          $ r%}t          /                    d,|           Y d	}~nd	}~ww xY w| j	        j7        }|d-k    r"| 8                    | j9         d.| d/d#0           tu          ||pd(| j;        pd	1          }|| j	        _<        d| j	        _=        	 dd2l>m?}  ||           n# t          $ r Y nw xY wt                              d3| j        pd|t          |          |d           ||fS )4u  Compress conversation context and split the session in SQLite.

    Args:
        agent: The owning :class:`AIAgent`.
        messages: Current message history (will be summarised).
        system_message: Current system prompt; rebuilt after compression.
        approx_tokens: Pre-compression token estimate, logged for ops.
        task_id: Tool task scope (used for clearing file-read dedup state).
        focus_topic: Optional focus string for guided compression — the
            summariser will prioritise preserving information related to
            this topic.  Inspired by Claude Code's ``/compact <focus>``.

    Returns:
        ``(compressed_messages, new_system_prompt)`` tuple.
    zPcontext compression started: session=%s messages=%d tokens=~%s model=%s focus=%rnoner   unknownuT   🗜️ Compacting context — summarizing earlier conversation so I can continue...)current_tokensrV   )r`   _last_summary_errorN!_last_compression_summary_warningu    ⚠ Compression summary failed: z%. Inserted a fallback context marker._last_aux_model_failure_model_last_aux_model_failure_error_last_aux_fallback_warning_keyu"   ℹ Configured compression model 'z
' failed (zunknown erroruS   ). Recovered using main model — check auxiliary.compression.model in config.yaml.user)rolecontentr   z%Y%m%d_%H%M%Sr=      HERMES_SESSION_IDr   )_SESSION_IDsession_z.jsonFHERMES_SESSION_SOURCEcli)
session_idsourcer    model_configparent_session_idTz,Could not propagate title on compression: %suK   Session DB compression split failed — new session will NOT be indexed: %sold_session_idon_session_startr   )boundary_reasonrs   z1context engine on_session_start (compression): %s)rr   resetreasonz2memory manager on_session_switch (compression): %s   u   ⚠️  Session compressed u>    times — accuracy may degrade. Consider /new to start fresh.)force)system_prompttools)reset_file_dedupz?context compression done: session=%s messages=%d->%d tokens=~%s)@lenr,   inforo   r    r+   _memory_manageron_pre_compressr)   r2   compress	TypeErrorr/   rb   _emit_warningre   _todo_storeformat_for_injectionappend_invalidate_system_prompt_build_system_prompt_cached_system_prompt_session_dbget_session_titlecommit_memory_sessionend_sessionr   nowstrftimeuuiduuid4hexosenvirongateway.session_contextrk   setlogs_dirsession_log_file_session_db_createdcreate_sessionplatformget_session_init_model_configget_next_title_in_lineageset_session_titler1   r9   update_system_prompt_last_flushed_db_idxr-   localshasattrrt   on_session_switchcompression_count_vprint
log_prefixr
   r{   last_prompt_tokenslast_completion_tokenstools.file_toolsr|   )r   rW   rY   rT   rU   rV   _pre_msg_count
compressedsummary_error_aux_fail_model_aux_fail_err_aux_keytodo_snapshotnew_system_prompt	old_titlers   rk   	new_titlee_old_sid_ce_err_me_err_cc_compressed_estr|   s                            rM   compress_contextr      s   0 ]]N
KKZ"FN -<=9ek	   
^  
  	!11(;;;; 	 	 	D	_-66xP]kv6ww

 _ _ _ -66xP]6^^


_
 E46KTRRM 5=tDDUU6CE36= 6 6 6   "%":<[]abb 8:Y[_`` 		'7Hu>EEQQ7?4##H H H%8H H H   %::<<M F6mDDEEE	##%%%22>BB"3E %m$	m);;E<LMMI''111))%*:MJJJ"-N"*,.."9"9/"J"JccTZ\\M]^`_`^`MaccE.3.>BJ*+?????? 01111    &+^6XAQ6X6X6X%XE"(-E%,, +~W8OQV)W)Wk"="0 -    )-E% TT % 1 K KI V VI%778H)TTTT"I. T T TLL!OQRSSSSSSSST2253CEVWWW)*E&& 	m 	m 	mNNhjkllllllll	m	S88<< 011 	 8:LMM 	$55 &B -' 6   
  S S SH'RRRRRRRRS
T88<< 011 	- 	!33 &B"*$	 4     T T TI7SSSSSSSST 
"
4C
axx C CC C C C 	 	
 	
 	
 4'-2k!T  O
 3BE/67E3
555555!!!!    KKI"FNC
OO  
 (((s   A9 9
BB
B( (&CC:B=P 8 K P 
K&#P %K&&A>P %:N  P  O1OP O*P 
P0P++P04AR 
S R;;S AT 
UT>>U.W   
WWapi_messagesboolc                   | sdS 	 ddl m n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY wdd}dfd}| D ]}t          |t                    s|                    d          }t          |t                    sC|D ]}t          |t                    s|                    d          }|dvr2|                    d          }t          |t                    r.|                    dd          }	 ||	          }
|
r
|
|d<   |dz  }t          |t                    r ||          }
|
r
|
|d<   |dz  }|rt          
                    d|dz             |dk    S )uw  Re-encode all native image parts at a smaller size to recover from
    image-too-large errors (Anthropic 5 MB, unknown other providers).

    Mutates ``api_messages`` in place. Returns True if any image part was
    actually replaced, False if there were no image parts to shrink or
    Pillow couldn't help (caller should surface the original error).

    Strategy: look for ``image_url`` / ``input_image`` parts carrying a
    ``data:image/...;base64,...`` payload.  For each one whose encoded
    size exceeds 4 MB (a safe target that slides under Anthropic's 5 MB
    ceiling with header overhead), write the base64 to a tempfile, call
    ``vision_tools._resize_image_for_vision`` to produce a smaller data
    URL, and substitute it in place.

    Non-data-URL images (http/https URLs) are not touched — the provider
    fetches those itself and the size limit is different.
    Fr   )_resize_image_for_visionu6   image-shrink recovery: vision_tools unavailable — %sNi  @ urlr.   r   r[   c                l   t          | t                    r|                     d          sdS t          |           k    rdS 	 |                     d          \  }}}d}|                    d          rZ|t          d          d                             dd          d                                         }|                    d          r|}ddl}|                    |          }d	d
ddddd	                    |d          }t          j        d|d          }		 |	                    |           |	                                  t          |	j                  |          }
	 t          |	j                                      d           nN# t"          $ r Y nBw xY w# 	 t          |	j                                      d           w # t"          $ r Y w w xY wxY w|
r t          |
          t          |           k    rdS |
S # t"          $ r&}t$                              d|           Y d}~dS d}~ww xY w)z8Return a smaller data URL, or None if shrink can't help.zdata:Nr   
image/jpeg;   r   zimage/z.pngz.gifz.webpz.jpgz.bmp)z	image/pngz	image/gifz
image/webpr   z	image/jpgz	image/bmphermes_shrink_F)prefixsuffixdelete)	mime_typemax_base64_bytesT)
missing_oku.   image-shrink recovery: re-encode failed — %s)
isinstancer.   
startswithr}   	partitionsplitstripbase64	b64decoder   tempfileNamedTemporaryFilewritecloser   nameunlinkr)   r,   r-   )r   headerr=   datamime	mime_part_b64rawr   tmpresizedrL   r   target_bytess               rM   _shrink_data_urlz<try_shrink_image_parts_in_messages.<locals>._shrink_data_url  sy   #s## 	3>>'+B+B 	4s88|##4#	!mmC00OFAtD  )) %"3w<<==177Q??BHHJJ	''11 %$D!!!!..&&C#&$6  c$  -'u  C		#		22NN"%1  NN))T)::::    DNN))T)::::    D c'llc#hh66tN 	 	 	NNKSQQQ44444	sz   CH A	F &(F H 
FH FH G!(G
	G

GGGG%H H 
H3H..H3rh   type>   	image_urlinput_imager   r   r   zGimage-shrink recovery: re-encoded %d image part(s) to fit under %.0f MBi   )r   r.   r   r[   )tools.vision_toolsr   r)   r,   r-   r   dictr   rX   r.   r~   )r   rL   changed_countr   r>   rh   partptypeimage_valuer   r   r   r   s              @@rM   "try_shrink_image_parts_in_messagesr     s   $  u???????   OQTUUUuuuuu #LM* * * * * * *X  ' '#t$$ 	'')$$'4(( 	 	' 	'DdD)) HHV$$E888((;//K +t,, 
'!ooeR00**3// ')0K&!Q&MK-- '**;77 '(/D%!Q&M'	'*  
U<;7	
 	
 	
 1s    
?:?)rN   rR   r   r   )r   r   r   r   )r   r   rW   rX   rY   r.   rT   rZ   rU   r.   rV   r[   r   r\   )r   rX   r   r   )__doc__
__future__r   loggingr   r   r   r   pathlibr   typingr   r   r   r	   r'   r
   	getLogger__name__r,   rN   rR   r   r   __all__     rM   <module>r      s>   8 # " " " " "  				               - - - - - - - - - - - - > > > > > >		8	$	$r
 r
 r
 r
j   . $(!%@) @) @) @) @) @)Fn n n nb  r   