
    [j;                     "   d Z ddlZddlZddlmZmZ ddlmZ  ej        e	          Z
eeegdf         Zeegdf         Zeg ef         ZdZdZdefdZdefd	Z	 	 	 	 dd
ededee         dee         dedee         dee         fdZd Z	 	 	 	 dded
ededee         dedee         dee         ddfdZ	 	 	 	 dded
ededee         dedee         dee         ddfdZ	 	 	 	 dded
edededee         dedee         dee         ddfdZdS )zAuto-generate short session titles from the first user/assistant exchange.

Runs asynchronously after the first response is delivered so it never
adds latency to the user-facing reply.
    N)CallableOptional)call_llma8  Generate a short, descriptive title (3-7 words) for a conversation that starts with the following exchange. The title should capture the main topic or intent. Write the title in the same language the user is writing in. Return ONLY the title text, nothing else. No quotes, no punctuation at the end, no prefixes.a  Generate a short, descriptive title (3-7 words) for a conversation that starts with the following exchange. The title should capture the main topic or intent. Write the title in {language}. Return ONLY the title text, nothing else. No quotes, no punctuation at the end, no prefixes.returnc                     	 ddl m}  t           |             pi                     d          pi                     di                               dd                                                    S # t
          $ r Y dS w xY w)zDReturn configured title language, or empty string to match the user.r   load_config	auxiliarytitle_generationlanguage )hermes_cli.configr	   strgetstrip	Exceptionr   s    7/home/ice/.hermes/hermes-agent/agent/title_generator.py_title_languager   +   s    	111111kmm!r&&{339rS#R((SR  
 
 %''		
    rrs   A-A0 0
A>=A>c                  "   	 ddl m}  ddlm}  |             }|                    d          pi                     d          pi } ||                    d          d          S # t
          $ r  t                              d	d
           Y dS w xY w)z=Return whether automatic session title generation is enabled.r   )load_config_readonly)is_truthy_valuer
   r   enabledT)defaultz'Failed to read title_generation.enabledexc_info)r   r   utilsr   r   r   loggerdebug)r   r   configtitle_configs       r   _auto_title_enabledr!   9   s     	;:::::))))))%%''

;//52::;MNNTRT|//	::DIIII   >NNNtts   A!A$ $&BBuser_messageassistant_responsetimeoutfailure_callbackmain_runtimeruntime_validatorc                 v   t                      st                              d           dS |T	  |            st                              d           dS n,# t          $ r t                              dd           Y nw xY w| r
| dd         nd}|r
|dd         nd}t	                      }|rt
                              |	          nt          }	d
|	ddd| d| dg}
	 t          d|
dd||          }|j	        d         j
        j        pd}ddlm}  |d|                                          }|                    d          }|                                                    d          r|dd                                         }t#          |          dk    r|dd         dz   }|r|ndS # t          $ r~}t                              d|           t                              dd           |:	  |d|           n,# t          $ r t                              dd           Y nw xY wY d}~dS d}~ww xY w)uA  Generate a session title from the first exchange.

    Uses the main runtime's model when available, falling back to the
    auxiliary LLM client (cheapest/fastest available model).
    Returns the title string or None on failure.

    ``failure_callback`` is invoked with ``(task, exception)`` when the
    auxiliary call raises — the caller typically wires this to
    ``AIAgent._emit_auxiliary_failure`` so the user sees a warning instead
    of silently accumulating untitled sessions.

    ``runtime_validator`` is called right before the LLM request. If it
    returns False (e.g. the user's model was switched since the background
    thread captured its runtime snapshot), the call is skipped silently —
    no request is sent, so a stale title request can't reload a model the
    runtime already unloaded (#19027).
    <Auto-title skipped: auxiliary.title_generation.enabled=falseNz:Title generation skipped: runtime validator returned Falsez*Title runtime validator raised; proceedingTr   i  r   )r   system)rolecontentuserzUser: z

Assistant: r   g333333?)taskmessages
max_tokenstemperaturer$   r&   r   )strip_think_blocksz"'ztitle:   P   M   z...zTitle generation failed: %szTitle generation tracebacktitle generationz(Title generation failure_callback raised)r!   r   r   r   r   _TITLE_PROMPT_PINNED_LANGUAGEformat_TITLE_PROMPTr   choicesmessager,   agent.agent_runtime_helpersr2   r   lower
startswithlenwarning)r"   r#   r$   r%   r&   r'   user_snippetassistant_snippetr   promptr/   responser,   r2   titlees                   r   generate_titlerG   J   s   2    STTTt$	V$$&& YZZZt  	V 	V 	VLLEPTLUUUUU	V
 *6=<%%2L4FN*4C400B  HHPc*1181DDDVcF f--$]\$]$]J[$]$]^^H
$#%
 
 
 "1%-5; 	CBBBBB""4117799E"";;==##H-- 	&!""IOO%%Eu::??#2#J&E'uu4' 
 
 
 	4a8881DAAA'X  !3Q7777 X X XGRVWWWWWXttttt
sN   $A &A>=A> CF0 0
H8:9H34H H3&H*'H3)H**H33H8c                      t           dd           fd}	  ||          S # t          $ r6 t           dd          }|  ||          }|r||k    r  ||          cY S w xY w)u  Persist a generated title, recovering from duplicate-title collisions.

    The write goes through ``set_auto_title_if_empty`` (predicate + write in
    one transaction) so a manual ``/title`` set while LLM generation was in
    flight is never overwritten — a plain ``set_session_title`` fallback keeps
    older stores working. ``set_session_title`` raises ValueError when the
    title would collide with another session (the unique-title index). Rather
    than swallow it and leave the session untitled (#50537), append a #N
    suffix via get_next_title_in_lineage() when the store supports lineage
    dedup; otherwise re-raise so the caller can decide.

    Returns the title actually persisted, or None when a concurrent manual
    title won the race (nothing was written).
    set_auto_title_if_emptyNc                     * |           st                               d           d S | S                     |           }|du rt          d d          | S )Nz\Skipping auto-generated session title because a title was set while generation was in flightFzsession z not found when storing title)r   r   set_session_titleRuntimeError)tok	atomic_fn
session_db
session_ids     r   _setz$_persist_session_title.<locals>._set   s     9Z++  =   tH))*a88;;D:DDD       get_next_title_in_lineage)getattr
ValueError)rP   rQ   rE   rR   next_title_fndedupedrO   s   ``    @r   _persist_session_titlerY      s     
$=tDDI      $	tE{{   
,GNN -&& 	'U**tG}}s   
( =A('A(rQ   title_callbackc           
      d   	 t          | |||||||           dS # t          $ r}t                              d|           t                              dd           |:	  |d|           n2# t          $ r t                              dd           Y nw xY wY d}~dS Y d}~dS Y d}~dS d}~ww xY w)	uw  Generate and set a session title if one doesn't already exist.

    Called in a background thread after the first exchange completes.
    Silently skips if:
    - session_db is None
    - session already has a title (user-set or previously auto-generated)
    - title generation fails
    - runtime_validator returns False (model was switched)

    Never lets an exception escape: this is a daemon-thread target, and an
    escaping exception would spray a raw traceback into the user's terminal
    via the default threading excepthook. The canonical trigger is the
    post-``hermes update`` stale-module window, where this function's lazy
    imports read NEW source from disk while already-cached modules
    (``agent.portal_tags`` etc.) are still the OLD version — the resulting
    ImportError repeats on every auto-title attempt until the long-running
    process restarts.
    r%   r&   rZ   r'   zeAuto-title failed (harmless; if this started after an update, restart the running Hermes process): %szAuto-title tracebackTr   Nr6   z"Auto-title failure_callback raised)_auto_title_sessionr   r   r@   r   )	rP   rQ   r"   r#   r%   r&   rZ   r'   rF   s	            r   auto_title_sessionr^      s6   8R-%)/		
 		
 		
 		
 		
 		
  R R R 	6	
 	
 	

 	+d;;;'R  !3Q7777 R R RADQQQQQR ('''''777777QQQQQQRs8    
B/9B*A,+B*,&BB*BB**B/c                 x   | r|sdS 	 |                      |          }|rdS n# t          $ r Y dS w xY wddlm}	 ddlm}
 |}	 |                     |          p|}n# t          $ r Y nw xY w |
|            |	| |           t          |||||          }|sdS 	 t          | ||          }|dS t          
                    d|           |;	  ||           dS # t          $ r  t          
                    dd	           Y dS w xY wdS # t          $ r&}t          
                    d
|           Y d}~dS d}~ww xY w)u9   Body of :func:`auto_title_session` — see its docstring.Nr   )set_accounting_context)set_conversation_context)r%   r&   r'   z Auto-generated session title: %szAuto-title callback failedTr   z&Failed to set auto-generated title: %s)get_session_titler   agent.aux_accountingr`   agent.portal_tagsra   get_conversation_rootrG   rY   r   r   )rP   rQ   r"   r#   r%   r&   rZ   r'   existingr`   ra   conversation_idrE   	persistedrF   s                  r   r]   r]   	  s     Z //
;; 	F	    <;;;;;:::::: O$:::FFT*   _--- :z222)!+  E  B*:z5II	F7CCC%Jy))))) J J J9DIIIIIIJ &%
  B B B=qAAAAAAAAABsZ   " 
00A 
A'&A'D	 /D	 C &D D	 DD	 	
D9D44D9conversation_historyc	           	      *   | r|r|r|sdS t          d |pg D                       }	|	dk    rdS t                      st                              d           dS t	          j        t          | |||f||||ddd          }
|
                                 dS )	u   Fire-and-forget title generation after the first exchange.

    Only generates a title when:
    - This appears to be the first user→assistant exchange
    - No title is already set
    Nc              3   L   K   | ]}|                     d           dk    dV   dS )r+   r-      N)r   ).0ms     r   	<genexpr>z#maybe_auto_title.<locals>.<genexpr>b  s7      \\qAEE&MMU[D[D[D[D[D[D[\\rS      r)   r\   Tz
auto-title)targetargskwargsdaemonname)sumr!   r   r   	threadingThreadr^   start)rP   rQ   r"   r#   ri   r%   r&   rZ   r'   user_msg_countthreads              r   maybe_auto_titler|   J  s    "  Z | CU  \\%9%?R\\\\\N    STTT!*l4FG 0(,!2	
 
   F LLNNNNNrS   )NNNN)__doc__loggingrw   typingr   r   agent.auxiliary_clientr   	getLogger__name__r   r   BaseExceptionFailureCallbackTitleCallbackboolRuntimeValidatorr9   r7   r   r!   floatdictrG   rY   r^   r]   listr|    rS   r   <module>r      s=         % % % % % % % % + + + + + +		8	$	$ C/56#% BH% c c     T    (  $2648V VVV e_V /	V
 V   01V c]V V V Vr, , ,h 37.2484R 4R4R 4R 	4R
 /4R 4R ]+4R   014R 
4R 4R 4R 4Rx 37.248>B >B>B >B 	>B
 />B >B ]+>B   01>B 
>B >B >B >BN 37.248. .. . 	.
 . /. . ]+.   01. 
. . . . . .rS   