o
    Wi                     @  s   d Z ddlmZ ddlmZmZ ddlmZmZ ddl	m
Z
mZ eddG dd	 d	ZeddG d
d dZeddG dd dZeddG dd dZeddG dd dZG dd dZdS )u   PromptBuilder: 组装给 LLM 的消息输入。

该模块负责将不同来源的信息（短期记忆、历史摘要、人设、长期记忆、当前问题）
拼装为标准 `Message` 列表，供 `LLM.chat()` 使用。
    )annotations)	dataclassfield)AnyOptional)MessageToolSpecT)slotsc                   @  s&   e Zd ZU dZded< dZded< dS )Summaryu   历史摘要条目。strcontent sourceN)__name__
__module____qualname____doc____annotations__r    r   r   4/home/dore/onebot/Bot_Ero/core/llm/prompt_builder.pyr
      s   
 r
   c                   @  s.   e Zd ZU dZded< ded< dZded< dS )Personau   人设信息。r   namedescriptionr   styleN)r   r   r   r   r   r   r   r   r   r   r      s
   
 r   c                   @  s2   e Zd ZU dZded< dZded< dZded< d	S )
Memoryu   长期记忆条目。r   r   g        floatscorer   r   N)r   r   r   r   r   r   r   r   r   r   r   r       s
   
 r   c                   @  sz   e Zd ZU dZded< eedZded< eedZded< d	Z	d
ed< eedZ
ded< d	Zded< eedZded< d	S )PromptRequestu   Prompt 构建请求。r   query)default_factorylist[Message]recent_messageslist[Summary]	summariesNPersona | Nonepersonalist[Memory]relevant_memorieszlist[ToolSpec] | Nonetoolszdict[str, Any]meta)r   r   r   r   r   r   listr!   r#   r%   r'   r(   dictr)   r   r   r   r   r   )   s   
 r   c                   @  s.   e Zd ZU dZded< ded< dZded< dS )	PromptResultu   Prompt 构建结果。r    messagesintused_tokens_estimateNzOptional[dict[str, Any]]
debug_info)r   r   r   r   r   r0   r   r   r   r   r,   =   s
   
 r,   c                   @  sB   e Zd ZdZdddZdddZdddZdddZdddZdS ) PromptBuilderu-   将多源上下文拼装成最终 messages。requestr   returnr,   c           	      C  s   |j  }|stdg }| |j}|dur|| | |j}|dur+|| | |j	}|dur:|| |
|j |td|d | |}|jt|jt|j	t|jt|jpag t|d}t|||dS )u0   核心接口：build(request) -> PromptResult。zPromptRequest.query is requiredNuser)roler   )r)   summary_countmemory_countrecent_message_count
tool_countfinal_message_count)r-   r/   r0   )r   strip
ValueError_build_persona_messager%   append_build_summary_messager#   _build_memory_messager'   extendr!   r   _estimate_tokensr)   lenr(   r,   )	selfr2   r   r-   persona_msgsummary_msg
memory_msgtoken_estimater0   r   r   r   buildL   s8   




	zPromptBuilder.buildr%   r$   Message | Nonec                 C  sR   |d u rd S dd|j  d|j g}|jr|d|j  tdd|ddS )	Nu?   你的人设信息如下，请在后续回答中保持一致：u
   - 名称: u
   - 描述: u
   - 风格: system
r%   r5   r   r   )r   r   r   r>   r   join)rD   r%   linesr   r   r   r=   y   s   

z$PromptBuilder._build_persona_messager#   r"   c                 C  sj   |sd S dg}t |ddD ]\}}|jrd|j dnd}|| d|j |  qtdd	|d
dS )Nu*   以下是历史摘要，请优先参考：   startu   （来源: u   ）r   . rK   rL   history_summariesrM   )	enumerater   r>   r   r   rN   )rD   r#   rO   idxitemr   r   r   r   r?      s   z$PromptBuilder._build_summary_messagememoriesr&   c                 C  s   |sd S dg}t |ddD ]9\}}g }|jr |d|jd |jr,|d|j  |r7dd| d	nd
}|| d|j |  qtdd|ddS )Nu<   以下是检索到的长期记忆，可在相关时引用：rP   rQ   zscore=z.4fzsource=z (z, )r   rS   rK   rL   r'   rM   )rU   r   r>   r   rN   r   r   )rD   rX   rO   rV   rW   extrasuffixr   r   r   r@      s   z#PromptBuilder._build_memory_messager-   r    r.   c                 C  s&   t dd |D }|d t|d  S )u  粗略估算 token 数，用于上层预算控制。

        这里采用保守的启发式估算，不追求与模型计费完全一致：
        - 文本长度 / 2 作为基础 token 估算；
        - 每条消息额外加 4 token 作为结构开销。
        c                 s  s    | ]}t |jV  qd S )N)rC   r   ).0msgr   r   r   	<genexpr>   s    z1PromptBuilder._estimate_tokens.<locals>.<genexpr>      )sumrC   )rD   r-   content_charsr   r   r   rB      s   zPromptBuilder._estimate_tokensN)r2   r   r3   r,   )r%   r$   r3   rJ   )r#   r"   r3   rJ   )rX   r&   r3   rJ   )r-   r    r3   r.   )	r   r   r   r   rI   r=   r?   r@   rB   r   r   r   r   r1   I   s    

-

r1   N)r   
__future__r   dataclassesr   r   typingr   r   core.llm.llmr   r   r
   r   r   r   r,   r1   r   r   r   r   <module>   s     