
    yjq                     `   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	 d dl
mZ d dlmZ d dlmZ d dlmZ d dlmZ d d	lmZmZ d d
lmZ d dlmZ d dlmZ d dlmZmZ d dl m!Z! d dl"m#Z# d dl$m%Z% d dl&m'Z'm(Z( d dl)m*Z* d dl+m,Z,m-Z- d dl.m/Z/m0Z0m1Z1 d dl2m3Z3 d dl4m5Z5m6Z6 ddl7m8Z8m9Z9  ej:        e;          Z< G d de          Z=de=dej=        fdZ>g dZ?defdZ@ejA        jB        ZBejA        jC        ZCd ZD G d! d"e          ZEd#eFd$eGd%eFdeFfd&ZHd#eFd$eGd%eFdeFfd'ZIedeGfd(            ZJedeGfd)            ZK e-d*+          	 dNdd,d#eFd-eGd.eFdz  d/eFdz  deeF         f
d0            ZL e-d1+          	 dNdd,d#eFd.eFdz  d/eFdz  deeF         fd2            ZMd/eFd3eFd4eGd5eGd6eFd7ejN        ddfd8ZOd/eFd3eFd4eGd5eGd6eFd9eEd7ejN        ddfd:ZPdd,d#eFd%eFdz  d9eEd-eGd6eFd;eGd<eFd=eGd/eFdz  deQe=eReGeGf         fd>ZSd?ed@e=d/eFd3eFddf
dAZTdeEjU        fd?ed/eFd3eFdBeGdz  d9eEdeFfdCZVeEjU        fd?ed/eFd3eFd9eEde=dz  f
dDZWd?ed/eFd3eFdeQe=dz  e=dz  f         fdEZXddFdGd?ed/eFd3eFdHeGdBeGdz  dIeRdeQej=        dz  eYe9j#                 f         fdJZZddFdGd?ed/eFd3eFdHeGdBeGdz  dIeRdeFfdKZ[dLeYe9j#                 deFfdMZ\dS )O    N)Enum)cache)cleandoc)	TypedDict)update)AsyncSession)schemas)ConfiguredModelSettingssettings)session_cache_key)
tracked_dbResourceNotFoundException)HonchoLLMCallResponsehoncho_llm_call)LLMTelemetryContext)Message)prometheus_metrics)AgentToolSummaryCreatedEventemit)CallPurpose)accumulate_metricconditional_observe)DeriverComponentsDeriverTaskTypes
TokenTypes)utc_now_iso)estimate_tokenstrack_deriver_input_tokens   )crudmodelsc                   P    e Zd ZU dZeed<   eed<   eed<   eed<   eed<   eed<   dS )	Summarya  
    A summary object. Stored in session metadata and used in a session's get_context.

    Attributes:
        content: The summary text.
        message_id: The primary key ID of the message that this summary covers up to.
        summary_type: The type of summary (short or long).
        created_at: The timestamp of when the summary was created (ISO format string).
        token_count: The number of tokens in the summary text.
    content
message_idsummary_type
created_attoken_countmessage_public_idN)__name__
__module____qualname____doc__str__annotations__int     </DATA/AppData/hermes/projects/honcho/src/utils/summarizer.pyr$   r$   '   s]         	 	 LLLOOOOOOr3   r$   sreturnc                     t          j        | d         | d         | d         | d         | d         |                     dd                    S )	Nr%   r&   r'   r(   r)   r*    r%   r&   r'   r(   r)   r*   )r	   r$   get)r5   s    r4   to_schema_summaryr;   ;   sR    ?)\?~&\?m$%% 3R88   r3   )get_summaryget_both_summariesget_summarized_historyget_session_contextget_session_context_formattedSummaryTyper$   r;   c                  $    t           j        j        S N)r   SUMMARYMODEL_CONFIGr2   r3   r4   _get_summary_model_configrF   S   s    ((r3   	summariesc                       e Zd ZdZdZdS )rA   honcho_chat_summary_shorthoncho_chat_summary_longN)r+   r,   r-   SHORTLONGr2   r3   r4   rA   rA   _   s        'E%DDDr3   rA   formatted_messagesoutput_wordsprevious_summary_textc           	      4    t          d| d|  d| d          S )z"Generate the short summary prompt.ae  
You are a system that summarizes parts of a conversation to create a concise and accurate summary. Focus on capturing:

1. Key facts and information shared (**Capture as many explicit facts as possible**)
2. User preferences, opinions, and questions
3. Important context and requests
4. Core topics discussed

If there is a previous summary, ALWAYS make your new summary inclusive of both it and the new messages, therefore capturing the ENTIRE conversation. Prioritize key facts across the entire conversation.

Provide a concise, factual summary that captures the essence of the conversation. Your summary should be detailed enough to serve as context for future messages, but brief enough to be helpful. Prefer a thorough chronological narrative over a list of bullet points.

Return only the summary without any explanation or meta-commentary.

<previous_summary>
%
</previous_summary>

<conversation>

</conversation>

Hard limit: P words maximum. If needed, drop lower-priority detail to stay within the limit.
crM   rN   rO   s      r4   short_summary_promptrW   d   sM        & ' , -     r3   c           	      4    t          d| d|  d| d          S )z!Generate the long summary prompt.a  
You are a system that creates thorough, comprehensive summaries of conversations. Focus on capturing:

1. Key facts and information shared (**Capture as many explicit facts as possible**)
2. User preferences, opinions, and questions
3. Important context and requests
4. Core topics discussed in detail
5. User's apparent emotional state and personality traits
6. Important themes and patterns across the conversation

If there is a previous summary, ALWAYS make your new summary inclusive of both it and the new messages, therefore capturing the ENTIRE conversation. Prioritize key facts across the entire conversation.

Provide a thorough and detailed summary that captures the essence of the conversation. Your summary should serve as a comprehensive record of the important information in this conversation. Prefer an exhaustive chronological narrative over a list of bullet points.

Return only the summary without any explanation or meta-commentary.

<previous_summary>
rQ   rR   rS   rT   rV   s      r4   long_summary_promptrY      sM      " # * + 0 1     r3   c                  d    	 t          t          ddd                    S # t          $ r Y dS w xY w)zQEstimate tokens for the short summary prompt (without messages/previous_summary).r8   r   rV      )r   rW   	Exceptionr2   r3   r4   $estimate_short_summary_prompt_tokensr]      sX    
 #%&(  
 
 	
    ss   ! 
//c                  d    	 t          t          ddd                    S # t          $ r Y dS w xY w)zPEstimate tokens for the long summary prompt (without messages/previous_summary).r8   r   rV   r[   )r   rY   r\   r2   r3   r4   #estimate_long_summary_prompt_tokensr`      sX    
#%&(  
 
 	
    ssr^   zCreate Short Summary)nameworkspace_nameinput_tokensprevious_summaryrc   c          
      :  K   t          t          |t          j        j                  dz            }|r|}nd}t          | ||          }t          t                      |t          j        j        t          |t          j
        j        d                     d {V S Ng      ?zSThere is no previous summary -- the messages are the beginning of the conversation.summary)rc   call_purposeparent_category)model_configprompt
max_tokens	telemetry)r1   minr   rD   MAX_TOKENS_SHORTrW   r   rF   r   r   SUMMARY_SHORTvalue)rM   rd   re   rc   rN   rO   rl   s          r4   create_short_summaryrs      s       s<)9)JKKdRSSL v 0 u!L*? F !.00#4%)$28%
 
 
		 	 	 	 	 	 	 	 	 	r3   zCreate Long Summaryc          
        K   t          t          j        j        dz            }|r|}nd}t	          | ||          }t          t                      |t          j        j        t          |t          j	        j
        d                     d {V S rg   )r1   r   rD   MAX_TOKENS_LONGrY   r   rF   r   r   SUMMARY_LONGrr   )rM   re   rc   rN   rO   rl   s         r4   create_long_summaryrw      s       x'7$>??L v 0 u L*? F !.00#3%)$17%
 
 
		 	 	 	 	 	 	 	 	 	r3   session_namer&   message_seq_in_sessionr*   configurationc           	      ,   K   j         j        du rdS j         j        z  dk    }j         j        z  dk    }|du r|du rdS |rE|rC fd} fd}	t	          j         |             |	            d           d{V  dS |r@t           t          j                   d{V  t          d	  d
 dd           dS |r@t           t          j
                   d{V  t          d	  d
 dd           dS dS )a  
    Create short/long summaries if thresholds met.

    This function checks for both short and long summary needs independently,
    without assuming any relationship between their thresholds.

    Args:
        workspace_name: The workspace name
        session_name: The session name
        message_id: The message ID
        message_seq_in_session: The sequence number of the message in the session
        message_public_id: The public ID of the message
        configuration: The resolved configuration for the message
    FNr   c            	         K   t          t          j                    d {V  t          d d dd           d S )Nr&   ry   r*   r'   rz   summary__long_summary_up_to_messagecount)_create_and_save_summaryrA   rL   r   rz   r&   r*   ry   rx   rc   s   r4   create_long_summary_taskz5summarize_if_needed.<locals>.create_long_summary_task0  s      *%'="3(-+          8>88J88,&	    r3   c            	         K   t          t          j                    d {V  t          d d dd           d S )Nr}   r~   r   short_summary_up_to_messager   )r   rA   rK   r   r   s   r4   create_short_summary_taskz6summarize_if_needed.<locals>.create_short_summary_taskA  s      *%'="3(.+          8>88J88-&	    r3   T)return_exceptionsr}   r~   r   r   r   r   )rh   enabledmessages_per_long_summarymessages_per_short_summaryasynciogatherr   rA   rL   r   rK   )
rc   rx   r&   ry   r*   rz   should_create_longshould_create_shortr   r   s
   ``````    r4   summarize_if_neededr   
  sy     , $-- 	!6!PPTUU  	!6!QQUVV  U""':e'C'C  J1 J	 	 	 	 	 	 	 	 	 	"	 	 	 	 	 	 	 	 	 	" n$$&&%%''"
 
 
 	
 	
 	
 	
 	
 	
 	
 	
 	
  	*%'="3(-+          8>88J88,&	     ! 	*%'="3(.+          8>88J88-&	    	 	r3   r'   c                  K   t                               d|j                   t          j                    }t          d          4 d{V }t          || ||           d{V }	|	r"|	d         }
|
|k    r	 ddd          d{V  dS |	r|	d         nd}|t          j        k    r|j	        j
        n|j	        j        }t          ||z
  dz   d          }t          j        || |||           d{V }|s/t                               d|           	 ddd          d{V  dS t!          |          }|d	         j        }|d	         j        dd
         }t'          |          }t)          d |D                       }|	r|	d         nd}||z   }ddd          d{V  n# 1 d{V swxY w Y   t+          ||||||||| 	  	         d{V \  }}}}|t          j        k    rt/                      }nt1                      }|st3          t4          j        t8          j        |t8          j        |t8          j        |i           t@          j!        j"        rKtG          j$        |d         t4          j        j%        tL          j'        j%        t8          j(        j%                   t          d          4 d{V }tS          ||| |           d{V  ddd          d{V  n# 1 d{V swxY w Y   tU          d|  d| |j         d|d         d           tU          d|  d| |j         d|d         d           t          j                    |z
  dz  }tU          d|  d| |j         d|d           |sLtW          tY          dddd| ||t'          |          ||t          j        k    rdnd|||||                     dS dS ) a   
    Create a new summary and save it to the database.
    1. Get the latest summary
    2. Get the messages since the latest summary
    3. Generate a new summary using the messages and the previous summary
    4. Save the new summary to the database
    zCreating new %s summaryzsummary.fetch_dataNr&   r%      )	start_seqend_seqz'No messages to summarize for message %s   c                     g | ]	}|j         
S r2   )r)   ).0messages     r4   
<listcomp>z,_create_and_save_summary.<locals>.<listcomp>  s    KKKww2KKKr3   r)   r   )	rM   rO   r'   rd   r*   last_message_idlast_message_content_previewmessage_countrc   )	task_type
components)r   r   
token_type	componentzsummary.saver~   r   _summary_textblob_summary_sizetokensi  _summary_creationmsderiver
summarizershortlong)run_id	iterationrj   
agent_typerc   rx   r&   r   ry   r'   rd   output_tokensprevious_summary_tokensmessage_tokensprompt_scaffold_tokens)-loggerdebugra   timeperf_counterr   r<   rA   rL   rh   r   r   maxr!   get_messages_by_seq_rangewarning_format_messagesidr%   lensum_create_summaryrK   r]   r`   r   r   rD   r   PROMPTMESSAGESPREVIOUS_SUMMARYr   METRICSENABLEDr   record_deriver_tokensrr   r   OUTPUTOUTPUT_TOTAL_save_summaryr   r   r   )rc   rx   r&   ry   r*   r'   rz   summary_startdblatest_summarylatest_summary_message_idrO   messages_per_summaryr   messagesrM   r   r   r   messages_tokensr   rd   new_summaryis_fallbackllm_input_tokensllm_output_tokensprompt_tokenssummary_durations                               r4   r   r   {  s     $ LL*L,=>>>%''M.// (A (A (A (A (A (A (A2*l 
  
 
 
 
 
 
 
  	(6|(D%(J66(A (A (A (A (A (A (A (A (A (A (A (A (A (A >L Uy 9 9QU {/// !;;&A 	
 .1EEI1MM	(,(F*)
 )
 )
 #
 #
 #
 #
 #
 #
  	NNDjQQQ=(A (A (A (A (A (A (A (A (A (A (A (A (A (AB .h77"2,/'/|';CRC'@$HKK(KKKLLCQ"X."?"?WX&)@@Q(A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A (A^ -3!!+'%A#%
 
 
 
	 
	 
	 
	 
	 
	  {(((<>>;==  "&.!(-!*O!24K	
 	
 	
 	
 # 	4!-0*28%,2+8>	    n-- 	 	 	 	 	 	 		        	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 0>00J00+++I	   0>00J00+++M"	   )++m;tC0>00J00///	    
 	(  )'-),!(mm'=(48I(I(IWWv-/(?.'4!  	
 	
 	
 	
 	
	
 
s2   *FBF"A*F
F(+F(1K
K&)K&r   r   r   c                d  K   d}	d}
d}d}	 |t           j        k    rt          | |||           d{V }	nt          | ||           d{V }	|	j        }|	j        }|	j        }|	j        }|                                sJt          	                    d|	j
                   d}
|dk    r	d| d| d	nd
}|rt          |          nd}d}d}n?# t          $ r2 t                              d           |dk    r	d| d| d	nd
}d}d}
Y nw xY wt          |||j        t!                      ||          |
||fS )aQ  
    Generate a summary of the provided messages using an LLM.

    Args:
        formatted_messages: Pre-formatted message string
        previous_summary_text: Optional previous summary to provide context
        summary_type: Type of summary to create ("short" or "long")
        input_tokens: Token count for input
        message_public_id: Public ID of the last message
        last_message_id: ID of the last message
        last_message_content_preview: Preview of last message content for fallback
        message_count: Number of messages for fallback

    Returns:
        A tuple of (Summary, is_fallback, llm_input_tokens, llm_output_tokens)
        where is_fallback indicates if the summary was generated using a
        fallback instead of an LLM call, and the token counts are from the LLM call
        (0 if fallback was used)
    NFr   rb   zNGenerated summary is empty (finish_reasons=%s). Falling back to basic summary.TzConversation with z messages about z...r8   zError generating summary!r9   )rA   rK   rs   rw   r%   r   rd   stripr   errorfinish_reasonsr   r\   	exceptionr$   rr   r   )rM   rO   r'   rd   r*   r   r   r   rc   responser   r   r   summary_textsummary_tokenss                  r4   r   r   "  s     @ 37HK,;,,,1"%-	        HH 1"%-        H  '!/#0$2 !!## 	"LL`'   K !1$$ f]eeD`eeee 
 ?KQ_\:::PQN  ! 	 	 	4555 q   baa@\aaaa 	
 	 	 &%+"}}&/	
 	
 	
 	 s   B<C	 	9DDr   rh   c                    K   ddl m} |d         }	 t          j        | ||           d{V }n,# |$ r$ t                              d| d|            Y dS w xY wi }|j                            t          i           }|||<   ||t          <   t          t          j                                      t          j        j        |k                                  t          j        j        |k                                  t          j        j                            d          |                    }	|                     |	           d{V  |                                  d{V  t'          ||          }
t)          j        |
           d{V  dS )	z
    Save a summary as metadata on a session.

    Args:
        db: Database session
        summary: The summary to save
        workspace_name: Workspace name
        session_name: Session name
    r   r   r'   NzCannot save summary: session z not found in workspace z||)internal_metadata)src.exceptionsr   r!   get_sessionr   r   r   r:   SUMMARIES_KEYr   r"   Sessionwhererc   ra   valuesopexecutecommitr   cache_clientdelete)r   rh   rc   rx   r   label_valuesessionupdate_dataexisting_summariesstmt	cache_keys              r4   r   r     s      988888 .)K(\>JJJJJJJJ$   bLbbR`bb	
 	
 	
 	 K 266}bII&-{#!3K 	v~	v~,>	?	?	v~"l2	3	3	$n>AA$GGTT 
 

 

	 	 **T


))++!.,??I

i
(
((((((((((s   / %AAcutoffc                   K   t          | |||           d{V }|r&t          j        | |||d         |           d{V }nt          j        | |||           d{V }t          |          }|rd|d          d| dS |S )	a  
    Get a summarized version of the chat history by combining the latest summary
    with all messages since that summary.

    Note: history is exclusive of the cutoff message.

    Args:
        db: Database session
        workspace_name: The workspace name
        session_name: The session name
        cutoff: (Optional) message ID to cutoff at
        summary_type: Type of summary to get ("short" or "long")

    Returns:
        A string formatted history text with summary and recent messages
    Nr&   )start_idend_id)r   z
<summary>
r%   z
</summary>
<recent_messages>
z
</recent_messages>
)r<   r!   get_messages_id_ranger   )r   rc   rx   r   r'   rh   r   messages_texts           r4   r>   r>     s     0  NL,OOOOOOOOG  
3\*
 
 
 
 
 
 
 
 
 3V
 
 
 
 
 
 
 
 

 %X..M 	 
    	 r3   c                    K   	 t          j        | ||           d{V }n# t          $ r Y dS w xY w|j                            t
          i           }|r	|j        |vrdS ||j                 S )a:  
    Get summary for a given session.

    Args:
        db: Database session
        workspace_name: The workspace name
        session_name: The session name
        summary_type: Type of summary to retrieve ("short" or "long")

    Returns:
        The summary data dictionary, or None if no summary exists
    N)r!   r   r   r   r:   r   rr   )r   rc   rx   r'   r   rG   s         r4   r<   r<     s      $(\>JJJJJJJJ$   tt %,$=$A$A-QS$T$TI *);;t\'((   ! 
//c                 4  K   	 t          j        | ||           d{V }n# t          $ r Y dS w xY w|j                            t
          i           }|                    t          j        j                  |                    t          j	        j                  fS )a  
    Get both short and long summaries for a given session.

    Args:
        db: Database session
        workspace_name: The workspace name
        session_name: The session name

    Returns:
        A tuple of the short and long summaries, or None if no summary exists
    N)NN)
r!   r   r   r   r:   r   rA   rK   rr   rL   )r   rc   rx   r   rG   s        r4   r=   r=     s       (\>JJJJJJJJ$   zz %,$=$A$A-QS$T$TI==*0119==AQAW3X3XXXr   Tr   include_summarytoken_limitr   c                  K   |dk    rdg fS d}|}d}|r=t          |dz            }	t          | ||           d{V \  }
}|r|d         nd}|
r|
d         nd}|rl||	k    rf||k    r`t          j        |d         |d         |d         |d         |d         |                    d	d
                    }||d         z
  }|d         }n|
rl||	k    rf|dk    r`t          j        |
d         |
d         |
d         |
d         |
d         |
                    d	d
                    }||
d         z
  }|
d         }nt
                              d|||           t          j        | |||||           d{V }||fS )a  
    Get session context similar to the API endpoint but for internal use.

    Args:
        db: Database session
        workspace_name: The workspace name
        session_name: The session name
        token_limit: Maximum tokens for the context
        cutoff: Optional message ID to stop at (exclusive)
        include_summary: Whether to include summary if available

    Returns:
        Tuple of (summary, messages) where summary is a Summary pydantic model (or None)
        and messages is the list of message objects
    r   Ng?r)   r%   r&   r'   r(   r*   r8   r9   zNo summary available for get_context call with token limit %s, returning empty string. Normal if brand-new session. long_summary_len: %s, short_summary_len: %s)r   r   r   )	r1   r=   r	   r$   r:   r   r   r!   r   )r   rc   rx   r   r   r   rh   r   messages_start_idsummary_tokens_limitlatest_short_summarylatest_long_summarylong_len	short_lenr   s                  r4   r?   r?   )  sO     0 aRxG!O .";#455:L;
 ;
 5
 5
 5
 5
 5
 5
11 :MS&}55RS;OV(77UV	  "	0009$$o+I6.|<0@.|</>"5"9"9:Mr"R"R  G *,?,NNO 3L A 	%.2F%F%F9WX==o,Y7/=1.A/=0?"6":":;NPR"S"S  G *,@,OOO 4\ BLL r	   /
"#        H Hr3   c                   K   |dk    rdS t          | |||||           d{V \  }}t          |          }|r|j        nd}	|	r|r	d|	 d| dS |	rd|	 dS |r|S dS )	z
    Get formatted session context as a string for internal use (e.g., deriver).

    This is a convenience wrapper around get_session_context that formats
    the output as a string.
    r   r8   r   Nz
<summary>
z
</summary>

<recent_messages>
z
</recent_messages>z
</summary>)r?   r   r%   )
r   rc   rx   r   r   r   rh   r   r   summary_contents
             r4   r@   r@     s       ar1
'        GX %X..M)08goobO =  
    	 
    	 
 rr3   r   c                 j    t          |           dk    rdS d                    d | D                       S )z{
    Format a list of messages into a string by concatenating their content and
    prefixing each with the peer name.
    r   r8   
c                 0    g | ]}|j          d |j         S )z: )	peer_namer%   )r   msgs     r4   r   z$_format_messages.<locals>.<listcomp>  s*    LLLC77#+77LLLr3   )r   join)r   s    r4   r   r     s;    
 8}}r99LL8LLLMMMr3   rC   )]r   loggingr   enumr   	functoolsr   inspectr   rU   typingr   
sqlalchemyr   sqlalchemy.ext.asyncior   srcr	   src.cache.clientr   
src.configr
   r   src.crud.sessionr   src.dependenciesr   r   r   src.llmr   r   src.llm.typesr   
src.modelsr   src.telemetryr   src.telemetry.eventsr   r   src.telemetry.events.llmr   src.telemetry.loggingr   r    src.telemetry.prometheus.metricsr   r   r   src.utils.formattingr   src.utils.tokensr   r   r8   r!   r"   	getLoggerr+   r   r$   r;   __all__rF   rD   MESSAGES_PER_SHORT_SUMMARYMESSAGES_PER_LONG_SUMMARYr   rA   r/   r1   rW   rY   r]   r`   rs   rw   ResolvedConfigurationr   r   tupleboolr   r   rK   r>   r<   r=   listr?   r@   r   r2   r3   r4   <module>r)     s                  ! ! ! ! ! !             / / / / / /       2 2 2 2 2 2 8 8 8 8 8 8 8 8 . . . . . . ' ' ' ' ' ' 4 4 4 4 4 4 : : : : : : : : - - - - - -       , , , , , , C C C C C C C C 0 0 0 0 0 0 H H H H H H H H         
 - , , , , , H H H H H H H H        		8	$	$    i   ( W_    	 	 	)#: ) ) ) )
 &-H $,F & & & & &$ & & &
  		   @  		   D c     S     0111 $( 
 "&        Dj 
 $J  3      21 F /000 $( "&	  Dj $J	
 3   10>nnn n  	n
 n 0n 
n n n nbd
d
d
 	d

  d
 d
 d
 0d
 
d
 d
 d
 d
b "&^ ^ ^^:^ ^ 	^
 ^ ^ #&^ ^ $J^ 7D#s"#^ ^ ^ ^B1)1)1) 1) 	1)
 
1) 1) 1) 1)p  + 17 777 7 $J	7
 7 	7 7 7 7| !, 1	) ))) ) 	)
 t^) ) ) )<YYY Y 7T>7T>)*	Y Y Y Y@  Y Y YYY Y 	Y $JY Y 7?T!4#778Y Y Y YD  . . ... . 	. $J. . 	. . . .bNtFN3 N N N N N N Nr3   