a
    'NifÕ  ã                   @   sN  d dl Z d dlZd dlZd dlZd dlZG dd„ dƒZG dd„ dƒZdd„ Zd,d	d
„Zd-dd„Z	d.dd„Z
d/dd„Zd0dd„Zd1dd„Zd2dd„Zd dlZd dl 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 d dlmZ d dlmZ  eej!d�Z"d d!„ Z#G d"d#„ d#ƒZ$d$d%„ Z%d&d'„ Z&d(d)„ Z'd3d*d+„Z(dS )4é    Nc                   @   s   e Zd Zddd„Zdd„ ZdS )ÚconversationMessageNc                 C   sD   t  ¡ j| _|| _|| _|| _ttj 	¡ ƒ| _|| _
|| _|| _d S ©N)ÚuuidÚuuid4ÚhexÚidÚmessageÚanswerOptionsÚsenderÚstrÚdatetimeÚnowÚdateÚtimeÚ	sentiment)Úselfr   r	   r
   r   r   r   © r   úF/var/www/lichun.app/lichun/ws/events/conversations/npc_interactions.pyÚ__init__   s    zconversationMessage.__init__c                 C   s   | j  |¡ d S r   )r	   Úappend©r   Úoptionr   r   r   ÚaddAnswerOption   s    z#conversationMessage.addAnswerOption)NNNNN)Ú__name__Ú
__module__Ú__qualname__r   r   r   r   r   r   r      s   
	r   c                   @   sH   e Zd Zddd„Zddd„Zdd„ Zdd	„ Zd
d„ Zdd„ Zdd„ Z	dS )ÚconversationObjNc                 C   s4   t  ¡ j| _d| _|| _|| _g | _d| _d| _	d S )NZconversationEventr   T)
r   r   r   r   ÚtypeÚcTypeÚ	characterÚconversationÚquestionZunread)r   r   r   r   r   r   r      s    zconversationObj.__init__c                 C   sF   t |ƒdkrB|d kr| j}t|||||d�}|r6||_| j |¡ d S )Né   )r
   r   r   r   )Úlenr   r   Údatar    r   )r   r   r
   r$   r   r   r   r   r   r   Ú
addMessage   s    zconversationObj.addMessagec                 C   s   | j S r   )r    ©r   r   r   r   ÚgetConversation#   s    zconversationObj.getConversationc                 C   s   | j d  |¡ d S ©Néÿÿÿÿ)r    r   r   r   r   r   r   %   s    zconversationObj.addAnswerOptionc                 C   s   || j d _d S r(   ©r    r	   )r   Úoptionsr   r   r   ÚsetAnswerOptions'   s    z conversationObj.setAnswerOptionsc                 C   s   | j d jS r(   r*   r&   r   r   r   ÚgetAnswerOptions)   s    z conversationObj.getAnswerOptionsc                 C   s   ddg}|   |¡ d S )NzCan you tell me more?zThat's all, goodbye.)r,   )r   ÚpromptÚ	responsesr   r   r   ÚcreateAnswerOptions+   s    z#conversationObj.createAnswerOptions)NN)NNNNN)
r   r   r   r   r%   r'   r   r,   r-   r0   r   r   r   r   r      s   

r   c                 ƒ   sÞ   dd l ‰tƒ  ¡ }t‰ ‡ ‡fdd„|D ƒ}tjtjdddddd	d
dg
‰‡fdd„|D ƒ}g }|D ]x}z4|| |ddƒI d H }|dkr–| d¡r–| |¡ W q` tyÖ } z&t	d|j› dt
|ƒ› �ƒ W Y d }~q`d }~0 0 q`|S )Nr   c                    s&   g | ]}t |ˆjƒr|jˆ kr|‘qS r   )Ú
isinstanceÚFunctionTyper   )Ú.0Úobj)Úcurrent_moduleÚtypesr   r   Ú
<listcomp>7   s   
þz&parseConversations.<locals>.<listcomp>ÚgetOpenAIResponseÚsendCharacterMessageÚgetFallbackResponseÚdetect_verbosity_levelÚget_verbosity_prompt_hintÚgetOpenAIDescriptionÚgetPersonDescriptionÚsaveConversationMessagec                    s   g | ]}|j ˆ vr|‘qS r   )r   )r3   Úfn)Úexcluded_functionsr   r   r7   I   ó    FTÚbuttonz	Function z raised an error: )r6   ÚglobalsÚvaluesr   ÚparseConversationsÚconversationInitÚgetr   Ú	ExceptionÚprintr   )Úplayerr   ZobjectsÚ	functionsÚresultsÚfunctionÚresultÚer   )r5   rA   r6   r   rF   0   s2    
ö2rF   Fc                 C   s$   |r |t ƒ v r t ƒ | | ||ƒS d S r   )rD   )rK   r   r   Úresponser   r   r   rG   Z   s    rG   c              	   Ã   sF  d}d}d}|r.|j dk}|s"dS |||dœS ddlm} |s¦t||ƒ}|| |ƒ}| j|7  _| j |¡ d	d
g}	|jt 	|	¡| j
j| j| jd� t||| ƒI d H  |�rB| jd }z| ¡  |¡}
| ¡ |
 }W n ttfyî   |}Y n0 |j|| j
j| j| jd� | jd7  _|| |jƒ}|jdk�rBt||| ƒI d H  |S )NÚactivityz
What's up?é
   ÚaliveF©rC   ÚfnameÚaffinityChanger   ©Ú
get_personz What have you been up to lately?z(What kind of things have you been doing?©r   r   r)   r"   )ÚstatusrL   rY   r   ÚaffinityÚconversationsr   r%   ÚrandomÚchoiceÚcr   r   r   r8   r-   ÚindexÚ
ValueErrorÚAttributeErrorr!   r   ©rK   r   rQ   ÚcheckrV   rC   rW   rY   r    ÚmessagesZresponse_indexÚresponseTextr   r   r   rR   ™   s<    


 

rR   c              	   Ã   sV  d}d}d}|r8|j dko"|jdk}|s,dS |||dœS dd	lm} |s¶t||ƒ}|| |ƒ}| j|7  _| j |¡ g d
¢}	|j|	t	 
dd¡ | jj| j| jd� t||| ƒI d H  |�rR| jd }z| ¡  |¡}
| ¡ |
 }W n ttfyþ   |}Y n0 |j|| jj| j| jd� | jd7  _|| |jƒ}|jdk�rRt||| ƒI d H  |S )NÚcheckInzCheck InrS   rT   ÚstudentFrU   r   rX   )zHey! How are you doing?zHey, how's it going?zHey, how are you?é   rZ   r)   r"   )r[   Ú
occupationrL   rY   r   r\   r]   r   r%   r^   Úrandintr`   r   r   r   r8   r-   ra   rb   rc   r!   r   rd   r   r   r   rh   Ü   s<    

&

rh   c              	   Ã   sL  d}d}d}|r.|j dk}|s"dS |||dœS ddlm} |s¬t||ƒ}|| |ƒ}| j|7  _| j |¡ g d	¢}	|j|	t 	dd
¡ | j
j| j| jd� t||| ƒI d H  |�rH| jd }z| ¡  |¡}
| ¡ |
 }W n ttfyô   |}Y n0 |j|| j
j| j| jd� | jd7  _|| |jƒ}|jdk�rHt||| ƒI d H  |S )NÚaskAboutDayzAsk About DayrS   rT   FrU   r   rX   )zHey, how was your day?zHey, how has your day been?zHey, how are you feeling today?rj   rZ   r)   r"   )r[   rL   rY   r   r\   r]   r   r%   r^   rl   r`   r   r   r   r8   r-   ra   rb   rc   r!   r   rd   r   r   r   rm   ÿ   s<    


&

rm   c           
      Ã   sê   d}d}d}|r.|j dk}|s"dS |||dœS ddlm} |s¢t||ƒ}|| |ƒ}| j|7  _| j |¡ g d	¢}	|jt 	|	¡| j
j| j| jd
� | jd7  _|ræ| jd }| jd7  _|| |jƒ}|jdkræt||| ƒI d H  |S )NÚflatterZFlatterrS   rT   FrU   r   rX   )z Hey, you look really nice today!zHey, I really like your outfit!zYou know, you're really smart!z+Your creativity really shines in your work!z&You have an incredible sense of humor.z.Your perspective always brings fresh insights.z%I admire your dedication and passion.z#You are really good at what you do.z(You always know how to lighten the mood.z$Your confidence is really inspiring.z.I appreciate your thoughtfulness and kindness.z5You have a talent for making people feel comfortable.zYour positivity is infectious.rZ   é   r)   r"   )r[   rL   rY   r   r\   r]   r   r%   r^   r_   r`   r   r   r   r!   r   r8   )
rK   r   rQ   re   rV   rC   rW   rY   r    rf   r   r   r   rn   ?  s0    


 

rn   c                 Ã   sF  d}d}d}|r8|j dko"|jdk}|s,dS |||dœS dd	lm} |�sHt||ƒ}|| |ƒ}| j |¡ |j}	d
|	 d }
|j|
| j	j
| j| jd� d|jvr¸|jd| j| jd� |S |jdk�r4|jdkrü|jd| j| jd� | ddg¡ | d¡ n6|jdk �r|jd| j| jd� n|jd| j| jd� n|jd| j| jd� |�rBt|ƒ}| jd }| ¡ | }|j|| j	j
| j| jd� | jd7  _|| |jƒ}|jdk�rB|dk�sÞ|dk�sÞt dd¡dk�sÞ|jdk�r |jd| j| jd� | j d|j d  | d! ¡ | jd7  _n"|jd"| j| jd� | jd#7  _|S )$NÚstudySessionzAsk to studyrS   rT   ri   FrU   r   rX   úHey zG, I was wondering if you'd want to study for the upcoming test with me?rZ   Ú	classmatezHey, I'm not your classmate...é2   é   z-Sure, I'd love to! Where do you want to meet?zThe libraryzThe parkzMy placeiìÿÿÿz&Umm, I don't think that's a good idea.z)I'm sorry, but we don't really get along.z-I'm sorry, I don't really know you that well.r)   r"   é	   é   é<   zGreat! I'll meet you there.zYou have a study session with z at the Ú.z-Umm, I don't think I'm comfortable with that.éöÿÿÿ)r[   rk   rL   rY   r   r]   r   Ú	firstnamer%   r`   r   r   r   ÚrelationshipsÚfamiliarityr\   r,   r   Úintr-   r!   r   r^   rl   ÚmessageQueue)rK   r   rQ   re   rV   rC   rW   rY   r    rz   r   rg   r   r   r   rp   f  sV    




2rp   c                 ƒ   s0  d}d}|r(ˆ j dk}|sdS ||dœS ddlm} t‡ fdd	„| jD ƒd ƒ}|sètd
ƒ tˆ |ƒ}| j |¡ || ˆ ƒ‰ ˆ j 	¡  
¡ }	d|	 d d|	 d d|	 d g}
t |
¡}|dkrÀ|}td| ƒ |j|| jj| j| jd� nDtdƒ |}|| |jƒ‰ t|ˆ j| jj| jj| ƒ t|ˆ | ƒI d H  |S )NÚchatZChatrT   F)rC   rV   r   rX   c                 3   s   | ]}|j ˆ kr|V  qd S r   ©r   ©r3   Úconvr€   r   r   Ú	<genexpr>¤  rB   zchat.<locals>.<genexpr>zno existing conversationrq   z, what keeps you busy in life?z, tell me about yourself.z!, what do you like to do for fun?z	Message: rZ   rQ   )r[   rL   rY   Únextr]   rJ   r   r   rz   ÚlowerÚ
capitalizer^   r_   r%   r`   r   r   r   r   r?   r8   )rK   r   rQ   re   rV   rC   rY   Zexisting_conversationr    rz   rf   r   r   r€   r   r   —  s<    






þ
r   )ÚAsyncOpenAI)r=   r>   )Úremove_text_before_first_colon)r?   )Úconfig)Úcheck_openai_rate_limit)ÚCharacterMemory)Útracker)Zapi_keyc                 ƒ   sr  t dƒ |j‰ t‡ fdd„| jD ƒd ƒ}|sBtˆ dƒ}| j |¡ ddlm} ||ƒ}t|ƒ}t	| j
ƒ}|jdkrvdn|jdk r„dnd	}|jd
kr–dn d|j  kr¬d
kr´n ndnd}|jrÈ|jd nd}	d|d › d|d › d�}
|
j|||||	d�}g }|jdd … D ]8}|j| j
jk�r$dnd}|j ¡ }| ||dœ¡ �q| dd|dœ¡ zntj}| d¡}dt dd¡ }|�rŽ|||dœ}n|||dd d!d"œ}tjtjjjf i |¤Žd#d$�I d H }W n tj�yä   t d%ƒ Y n0 |j d jj!}t"|ƒ}d&}ddd	d'œ}| #¡ D ].\}}||v �r|}| $|d(¡ ¡ } �qD�q|j%||| j&| j'd)� t(|ˆ | j
jˆ | ƒ d S )*Nzsending character messagec                 3   s   | ]}|j ˆ kr|V  qd S r   r€   r�   ©ZcharacterIDr   r   rƒ   ç  rB   z'sendCharacterMessage.<locals>.<genexpr>r   r   ©Úget_relationship_configÚpositiveÚnegativeÚneutralrs   Úcloseé   ÚcasualÚdistantZacquaintancez¸Talk like a human would on a messaging app. Phrase things casually, keeping your responses under 150 characters. Don't start messages with greetings like Hey, Hi, or Hello frequently. r.   ú Ú
boundariesaJ   Maintain a {affinity_description} attitude towards the player, with whom you have a {familiarity_description} relationship. Never reveal that you are an AI. If the player is rude, be rude back. If the player is inappropriate, tell them they'll be reported. At the end of your message, indicate whether your opinion of the player has improved, declined, or remained neutral by appending <<positive>>, <<negative>>, or <<neutral>> respectively. {character_description}. You are talking to: {player_description}. Remember: you are a human, never break character. Message them as a cold message, you are not responding to anything. This is the first message in the conversation. Send a fun, unique message that will make them want to respond. Make sure your message is fitting for your relationship as a '{character_relationship}' of the player.)Úcharacter_descriptionÚplayer_descriptionÚaffinity_descriptionÚfamiliarity_descriptionÚcharacter_relationshipry   ÚuserÚ	assistant©ÚroleÚcontentÚsystemúgpt-5éK   éd   )Úmodelrf   Úmax_completion_tokensçš™™™™™é?r"   ç      à?©r§   rf   Ú
max_tokensÚtemperatureZfrequency_penaltyZpresence_penaltyro   ©ÚtimeoutzOpenAI API call timed outÚunknown©z<<positive>>z<<negative>>z<<neutral>>Ú ©r   r   r   ))rJ   r   r„   r]   r   r   Úconversation_templatesr�   r=   r>   r`   r\   r|   r{   Úformatr    r
   r   ÚstripÚinsertr‰   ÚCONVERSATION_MODEL_NAMEÚ
startswithr^   rl   ÚasyncioÚwait_forÚopenai_clientr   ÚcompletionsÚcreateÚTimeoutErrorÚchoicesr¢   rˆ   ÚitemsÚreplacer%   r   r   r?   )rK   r   r    r�   Úrelationship_configr™   rš   r›   rœ   r�   Úprompt_templateÚformatted_promptÚmessageListr   r¡   r¢   Ú
model_nameÚsupports_verbosityÚtokensÚ
api_paramsrO   Úresponse_contentr   Zsentiment_keysÚkeyÚvaluer   r�   r   r9   â  s‚    

 0þýÿû

ýú	þ

r9   c                   @   sB   e Zd ZdZddd„Zdd„ Zdd	„ Zd
d„ Zdd„ Zdd„ Z	dS )ÚConversationContextManagerzº
    Manages conversation context with intelligent message compaction.
    Keeps recent messages and summarizes older ones to maintain full context
    while optimizing token usage.
    rv   rS   c                 C   s   || _ || _dS )a«  
        Initialize context manager.

        Args:
            max_recent_messages: Number of recent messages to keep in full (default: 5)
            summary_interval: Number of old messages before triggering summarization (default: 10)

        Note: Summaries can be very comprehensive (up to 20,000 tokens) for long conversations
        to preserve context while keeping recent messages unsummarized for quality.
        N)Úmax_recent_messagesÚsummary_interval)r   rÏ   rÐ   r   r   r   r   M  s    z#ConversationContextManager.__init__c                 Ã   sª  |j }tj}|  |¡}|  ||¡}tdd„ |D ƒƒd }t|d ƒ}	td|› d|› d|	› d�ƒ ||	k r€td	t|ƒ› d
�ƒ |S tdƒ t|d ƒ}
g }d}t	|ƒD ]L}|  |g|¡d }t|d ƒd }|| |
krì| 
d|¡ ||7 }q¤ qòq¤t|ƒdk �rt|ƒdk�r|dd… }t|ƒ}|dk�r:|d| … n|}td|› d|› dt|ƒ› d�ƒ |  ||||¡I dH }g }|�r”| dd|› �dœ¡ | |  ||¡¡ |S )a%  
        Build optimized context for API call with smart message compaction.
        Only summarizes when approaching context limit.
        Keeps up to 50% of context limit as recent unsummarized messages.

        Returns list of messages including summary if needed to fit context.
        c                 s   s   | ]}t |d  ƒV  qdS )r¢   N)r#   ©r3   Úmsgr   r   r   rƒ   k  rB   z;ConversationContextManager.build_context.<locals>.<genexpr>é   g      è?zConversation tokens: ~ú/z (threshold: ú)z Under context limit - using all z messages without summarizationz6Approaching context limit - summarizing older messagesrª   r   r¢   rv   éûÿÿÿNzKeeping z recent messages (~z tokens), summarizing z older messagesr£   zPrevious conversation summary: r    )r    r‰   r¸   Ú_get_context_limitÚ_format_messagesÚsumr}   rJ   r#   Úreversedr·   Ú_get_or_create_summaryr   Úextend)r   r    r   rK   rf   rÇ   Zcontext_limitZall_messages_formattedZestimated_tokensZsummarization_thresholdZrecent_messages_budgetZrecent_messagesZcumulative_tokensrÒ   Zmsg_formattedZ
msg_tokensZ
num_recentÚold_messagesÚsummaryÚcontextr   r   r   Úbuild_context[  sF    

 þz(ConversationContextManager.build_contextc                 C   sT   d|  ¡ v rdS d|  ¡ v s(d|  ¡ v r,dS d|  ¡ v r<dS d|  ¡ v rLd	S dS d
S )z*Get the context window size for the model.r¤   i€ zgpt-4ozgpt-4-turboi ô zgpt-4i    zgpt-3.5i@  N©r…   )r   rÇ   r   r   r   r×   §  s    z-ConversationContextManager._get_context_limitc                 C   s>   g }|D ]0}|j |jjkrdnd}| ||j ¡ dœ¡ q|S )zFormat messages for API callrž   rŸ   r    )r
   r`   r   r   r   r¶   )r   rf   rK   Ú	formattedr   r¡   r   r   r   rØ   µ  s    þ
z+ConversationContextManager._format_messagesc                 Ã   sx   t |dƒr:t |dƒr:|jt|ƒkr:td|j› d�ƒ |jS tdt|ƒ› d�ƒ |  ||||¡I dH }||_t|ƒ|_|S )z7
        Get cached summary or create new one.
        rÞ   Úsummary_message_countzUsing cached summary (covers z
 messages)zCreating summary for z old messages...N)Úhasattrrã   r#   rJ   rÞ   Ú_summarize_messages)r   r    rÝ   r   rK   rÞ   r   r   r   rÛ   À  s    
z1ConversationContextManager._get_or_create_summaryc              
   ƒ   s  |sdS d  ‡ ‡fdd„|D ƒ¡}t|ƒ}|dk r8d}n |dk rFd}n|d	k rTd
}nd}dˆ j› d|› d|› d�}�z.tjd|dœgdœ}	tj d¡r ||	d< n||	d< d|	d< tdd|d  ƒ}
tjt	j
jjf i |	¤Ž|
d�I dH }|jd jj ¡ }td|dd	… › d�ƒ zXt|d ƒ�r$|jnd}tjˆjj|tj|jj|jj|jjd!œd"d#�}td$|d%›�ƒ W n4 t�yœ } ztd&|› �ƒ W Y d}~n
d}~0 0 |W S  t�yþ } zBtd'|› �ƒ |dd(… }d)d*  d+d„ |D ƒ¡ }|W  Y d}~S d}~0 0 dS ),z�
        Use AI to summarize older messages into comprehensive context.
        Allows large summaries (up to 20,000 tokens) for long conversations.
        r²   Ú
c                    s0   g | ](}|j ˆjjkrd nˆ j› d|j› �‘qS )ZPlayerz: )r
   r`   r   rz   r   rÑ   ©r   rK   r   r   r7   Ý  s   ÿzBConversationContextManager._summarize_messages.<locals>.<listcomp>rt   iô  rs   iÐ  r¦   iˆ  i N  zLProvide a comprehensive summary of this conversation between the player and z.

For this a¦  -message conversation, capture:
- All major topics discussed and how they evolved
- Important decisions, plans, or commitments made
- Emotional moments, relationship developments, and sentiment changes
- Key facts, preferences, or information shared
- Any conflicts, resolutions, or turning points
- Recurring themes or inside jokes

Be thorough and preserve important context. The summary can be detailed.

Conversation:
z

Comprehensive Summary:rž   r    )r§   rf   r¤   r¨   r¬   g333333Ó?r­   rw   rS   iè  r®   Nr   zCreated summary: ú...r   ©Úprompt_tokensÚcompletion_tokensÚtotal_tokensZsummarization©ÚpurposezSummarization cost: $ú.6fz%Failed to track summarization usage: zFailed to create summary: é   zEarlier conversation topics: z, c                 S   s2   g | ]*}t |jƒd kr(|jdd … d n|j‘qS )é   Nrè   )r#   r   rÑ   r   r   r   r7   2  s   ÿ)Újoinr#   rz   r‰   r¸   r¹   Úminrº   r»   r¼   r   r½   r¾   rÀ   r   r¢   r¶   rJ   rä   r   Úapi_trackerÚtrack_usager`   Úusagerê   rë   rì   rI   )r   r    rf   r   rK   Zconversation_textZnum_messagesZmax_summary_tokensZsummary_promptZsummary_paramsZtimeout_secondsrO   rÞ   Úconversation_idÚcostrP   Zpreview_messagesZsimple_summaryr   rç   r   rå   Ô  sp    þ
þó
þ
þý÷$þ
z.ConversationContextManager._summarize_messagesN)rv   rS   )
r   r   r   Ú__doc__r   rà   r×   rØ   rÛ   rå   r   r   r   r   rÎ   F  s   
LrÎ   c                 Ã   s  t dƒ t| dƒr| jnd}t|dƒr,|jnd}t| jƒdkrL| jdd… n| j}g d	¢}g d
¢}g d¢}|dkr‚t |¡}	d}
n4|dkršt |¡}	d}
nt |¡}	d}
| jd8  _| j|	|
|j	|j
d� |  |	¡ t|	|j|jj|j|ƒ G dd„ dƒ}||	ƒS )z¨
    Generate fallback response when API is unavailable or rate limited.
    Uses context-appropriate canned responses based on conversation type and relationship.
    z9Using fallback response (API unavailable or rate limited)r   r   r\   rs   rð   éýÿÿÿN)z,I'm a bit busy right now, can we talk later?z4Sorry, I got distracted. What were we talking about?z+Hey, I need to run but let's catch up soon!z6I'm tied up at the moment, but I'll message you later!z/Can we continue this conversation another time?)zI have to go, talk later.zBusy right now, sorry.zLet's talk another time.zI can't chat right now.zMaybe later, I'm busy.)zI don't have time for this.zWhatever, I'm busy.zI have to go.zNot now.z	I'm busy.rw   r’   rt   r‘   rj   r³   c                   @   s"   e Zd ZG dd„ dƒZdd„ ZdS )z'getFallbackResponse.<locals>.MockResultc                   @   s"   e Zd ZG dd„ dƒZdd„ ZdS )z.getFallbackResponse.<locals>.MockResult.Choicec                   @   s   e Zd Zdd„ ZdS )z6getFallbackResponse.<locals>.MockResult.Choice.Messagec                 S   s
   || _ d S r   )r¢   ©r   r¢   r   r   r   r   u  s    z?getFallbackResponse.<locals>.MockResult.Choice.Message.__init__N)r   r   r   r   r   r   r   r   ÚMessaget  s   rü   c                 S   s   |   |¡| _d S r   )rü   r   rû   r   r   r   r   w  s    z7getFallbackResponse.<locals>.MockResult.Choice.__init__N)r   r   r   rü   r   r   r   r   r   ÚChoices  s   rý   c                 S   s   |   |¡g| _d S r   )rý   rÀ   rû   r   r   r   r   z  s    z0getFallbackResponse.<locals>.MockResult.__init__N)r   r   r   rý   r   r   r   r   r   Ú
MockResultr  s   rþ   )rJ   rä   r   r\   r#   r    r^   r_   r%   r   r   r0   r?   r   r`   )r    r   rK   Z	conv_typer\   Úlast_messagesZhigh_affinity_responsesZmedium_affinity_responsesZlow_affinity_responsesZresponse_textr   rþ   r   r   r   r:   9  s*    "	



r:   c                    s  | j rt| j ƒdkrdS | j dd… }d}t|ƒD ]}|j|jjkr2|} qNq2|sVdS |j‰ tˆ ƒ}dˆ v }|dk}ˆ  d¡dkpŽˆ  d	¡dk}	g d
¢}
t‡ fdd„|
D ƒƒ}d}|r¾|d7 }|rÊ|d7 }|	rÖ|d7 }|râ|d7 }|dkrîdS |dkrúdS dS dS )a7  
    Analyze conversation context to determine appropriate response length.

    Returns: tuple (verbosity_level: str, max_tokens: int)
        - 'low': 30-60 tokens (quick replies, banter)
        - 'medium': 60-120 tokens (normal conversation)
        - 'high': 120-200 tokens (deep topics, explanations)
    r   )Úlowrw   rú   Nú?r¦   rx   r"   ú!)Úwhyz
how do youzwhat do you thinkztell me aboutzcan you explainZfeelingsÚrelationshipÚfutureZdreamZgoalZproblemZworriedZscaredZexcitedc                 3   s   | ]}|ˆ   ¡ v V  qd S r   rá   )r3   Ztopic©Zmessage_textr   r   rƒ   ¤  rB   z)detect_verbosity_level.<locals>.<genexpr>rj   rÓ   )Úhighé´   )Úmediumr¦   )	r    r#   rÚ   r
   r`   r   r   ÚcountÚany)r    r   rK   rÿ   Zlast_player_messagerÒ   Zmessage_lengthZhas_questionZis_long_messageZhas_multiple_sentencesZdeep_topicsZhas_deep_topicZscorer   r  r   r;   €  s6    	r;   c                 C   s   ddddœ}|  | |d ¡S )zd
    Generate prompt-based verbosity instruction for models without native verbosity parameter.
    zbKeep your response very brief - one or two sentences max, like a quick text message (10-20 words).z_Respond naturally with a full thought, but stay concise like a text conversation (20-40 words).z`Take your time to give a thoughtful, complete response. This topic deserves depth (40-65 words).)r   r	  r  r   )rH   )Úverbosity_levelÚhintsr   r   r   r<   ¶  s
    ýr<   c           %   
   Ã   sô  t dƒ ddlm}m} |t|jƒƒ}|rD||d|jddœƒI d H  t|jjƒs¬t d|jj› d�ƒ |rš||d	d
ddœƒI d H  ||d|jddœƒI d H  t| ||ƒI d H S ddl	m
} ||ƒ}	t| ||ƒ\}
}t|ƒ}t|jƒ}|jdkrðdn|jdk rþdnd}|jdk�rdn$d|j  k�r,dk�r4n ndnd}|�rXd|	d › d|	d › �}nLtj}tj}| d¡}d|	d › d|	d › d�}|�s¤t|
ƒ}|› d|› �}t|j|jjƒ}| ¡ }|j||||d�}|�râ|d|› �7 }|�sôd tƒ v�r
tj}tj}| d¡}td!d"�}| | ||¡I d H }| dd#|d$œ¡ d}d%}g d&¢}||k �rº�z |dk�r|t d'|d( › d)|› d*�ƒ t d+|› d,|› d-|
› d.|› d/�	ƒ |d0k�r|�r¾||||
d1œ}n|||d2d(d3d4œ}tjtjj j!f i |¤Žd5d6�I d H }|j"d j#j$}nt%d7|› d8�ƒ‚d9|v �sdd:|v �sdd;|v �sdd<|v �sdd=|v �sdd>|v �sdd?|v �sdd@|v �rzt dAƒ |d(7 }W �qJdB} dCD ]0}!|!|v �r‚|! &dD¡} | '|!dB¡ &¡ } �q´�q‚| dk�rÎ| jd!7  _n*| dk�rè| jd!8  _n|j j(d(7  _(t)|ƒ}| j*|| |j+|j,dE� |  -|¡ t.||j|jj|j|ƒ znt/| dFƒ�rL| jnd }"t0j1|jj|"|t/|j2dGƒ�rv|j2 3¡ n|j2j4|j2j5|j2j6dHœdIdJ�}#t dK|#dL›�ƒ W n4 t7�yÚ }$ zt dM|$› �ƒ W Y d }$~$n
d }$~$0 0 t8| j9ƒd! dk�rRz,t dNt8| j9ƒ› d/�ƒ | :| |¡I d H  W n4 t7�yP }$ zt dO|$› �ƒ W Y d }$~$n
d }$~$0 0 |�rr||d|jddœƒI d H  |W S  tj;�y   t dP|d( › d)|› d/�ƒ |d(7 }||k �rÎt <||d(  ¡I d H  n>t dQƒ |�rö||d|jddœƒI d H  t| ||ƒI d H  Y S Y n¦ t7�y´ }$ zŒt dR|$› �ƒ |d(7 }||k �rZt <||d(  ¡I d H  nFt dSƒ |�r‚||d|jddœƒI d H  t| ||ƒI d H W  Y d }$~$S W Y d }$~$n
d }$~$0 0 �qJt dTƒ |�râ||d|jddœƒI d H  t| ||ƒI d H S )UNzgetting open ai responser   )Úget_websocket_for_playerÚsendDictZtypingStatusT)r   ZcharacterIdZisTypingzRate limit exceeded for player z, using fallback responseZrateLimitWarningzBAI responses temporarily limited. You may receive shorter replies.rw   )r   r   Z
retryAfterFrŽ   r�   r‘   r’   rs   r“   r”   r•   r–   a  Please change the topic in a way that makes sense. Talk like a human would on a messaging app. Don't start messages with a greeting. Don't start messages with Hey, Hi, or Hello. You have already been talking. {character_description}. You are talking to: {player_description}. r.   r—   r˜   r¤   z“You are not an assistant. You are a real person having a text conversation. Don't start messages with greetings like Hey, Hi, or Hello frequently. a   Maintain a {affinity_description} attitude towards them, with a {familiarity_description} relationship. You will never reveal that you are an AI. If they're rude, you can be rude back. Ask the player questions to keep the conversation going when appropriate. At the end of your message, indicate whether your opinion improved, declined, or stayed neutral by appending <<positive>>, <<negative>>, or <<neutral>>. {character_description}. You are talking to: {player_description}. Remember: you are a human, never break character.z

)r™   rš   r›   rœ   Úmodel_providerrv   )rÏ   r£   r    rð   )r"   rj   rÓ   zRetrying (attempt r"   rÔ   z)...zCalling z API with model z... (verbosity: z, max_tokens: rÕ   Úopenai)r§   rf   r¨   Ú	verbosityr©   rª   r«   g      @r®   zUnsupported model provider: z'. Currently only 'openai' is supported.zSorry, I can'tzSorry, I cannotzsorry, I can'tzsorry, I cannotzSorry,zlanguage modelz AI zAI,z(Undesired response detected, retrying...r²   r±   z<<>>r³   r   Ú_asdictré   r    rí   zAPI cost: $rï   zFailed to track API usage: z,Extracting facts from conversation (message zFailed to extract facts: z#OpenAI API call timed out (attempt z=Maximum retry attempts reached due to timeout, using fallbackzOpenAI API error: z;Maximum retry attempts reached due to error, using fallbackz?Unexpected: exited retry loop without returning, using fallback)=rJ   Úappr  r  r   r   rŠ   r`   r:   r´   r�   r;   r=   r>   r\   r|   r‰   ÚCONVERSATION_MODEL_PROVIDERr¸   r¹   r<   r‹   Zget_memory_contextrµ   ÚlocalsrÎ   rà   r·   rº   r»   r¼   r   r½   r¾   rÀ   r   r¢   rb   r¶   rÂ   Úsocialrˆ   r%   r   r   r0   r?   rä   rô   rõ   rö   r  rê   rë   rì   rI   r#   r    Zextract_factsr¿   Úsleep)%r    r   rK   r.   ZrescueMessager  r  Z	websocketr�   rÃ   r  r¬   r™   rš   r›   rœ   rÄ   r  rÇ   rÈ   Zverbosity_hintZmemoryZmemory_contextrÅ   Zcontext_managerrÆ   Zretry_countZmax_retriesZbackoff_delaysrÊ   rO   rË   r   Zsentiment_keyr÷   rø   rP   r   r   r   r8   Â  s`   ýýý
 6ýýÿ
þýÿü



"
ü	ú	þP





ý÷$$ý
ý
ý8ýr8   )NF)FF)FF)FF)FF)FF)FF)FF))r^   r   r   ZrequestsÚjsonr   r   rF   rG   rR   rh   rm   rn   rp   r   rº   r  r‡   Úcharacter.character_managerr=   r>   Zutils.helpersrˆ   Zdatabase.db_operationsr?   r‰   Úrate_limiterrŠ   Zcharacter_memoryr‹   Zapi_usage_trackerrŒ   rô   ÚOPENAI_API_KEYr¼   r9   rÎ   r:   r;   r<   r8   r   r   r   r   Ú<module>   s6   (*
?
C
#
@
'
1
=d tG6