
    ESj                     V   d 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
mZ d d	Zd
ddededefdZd Zd Zd Zd Zd Zd Zd Zd Zd Zedk(  rddlZ e e       j;                               D  cg c]  \  } }| j=                  d      s| c}} ZdZ eD ]  Z!	  e!         e"de!j2                          !  e"d e%e      e z
   d e%e       d        e&e rd      d      yc c}} w # e#$ r. e dz  Z  e"de!j2                           ejH                          Y w xY w)!u  Tone-aware TTS (TONE_TTS): the sensing → delivery bridge, in pure logic.

Two halves of the "voice presence" move, tested without any audio or model:
  1. voice.prosody's rolling ToneState — the EMA smooths Ahmed's recent vocal
     energy so one loud word can't flip the mood, and laughter recency fades.
  2. voice.tts_pocket's clip picker (_biased_stem) — a specific brain tag wins;
     a bouncy tag softens to `warm` when he's plainly down (never urgent/
     annoyed/shocked); an untagged line follows his tone; TONE_TTS=0 replays
     today's tag-only pick byte-for-byte.

Run: `python -m pytest test_tone.py`  (or `python test_tone.py` standalone).
    )SimpleNamespace)prosody)	ToneState)
tts_pocket)_biased_stem_NEVER_DAMP_TAG_TO_STEMreturnNc                  *    t               t        _        y)uH   Neutral tone state — the picker tests that read the global start here.N)r   r   _TONE_STATE     test_tone.py_resetr      s    #+Gr   Flaughtagsr   c                     t        j                  t        j                  t        |      d| i             t        j                         S )zDFold one synthetic read (just its tags) into the rolling tone state.r   )r   feats)r   _publish_toneProsodylist
tone_state)r   r   s     r   _pubr      s:    '//tDz18%0@B Cr   c                      t                t        d      D ]  } t        d        t        j                         j
                  sJ d       t        d      }|j                  rJ d       y )N   animatedz%sustained energy should read animatedflatz/a single flat turn must not flip a settled mood)r   ranger   r   r   updown)_sts     r   )test_ema_settles_and_one_word_cannot_flipr$   &   sX    
H1XZ ""K$KK"	fBwwIII;wr   c                      t                t        dd      } | j                  rJ d       t        d      D ]  }t        dd      }  | j                  r| j                  r| j
                  sJ y )Nr   slowz6one flat turn is 'down' at most, not yet 'really flat'r   )r   r   lowr   r!   r&   )r#   r"   s     r   test_flat_and_slow_read_downr(   /   sZ    
H	ff	BvvOOO:1X&&! 77rvv"''))'r   c                      t                t        dd      j                  sJ t               j                  sJ d       t               j                  rJ d       y )NlaughingTr   z-laughter should still register one turn lateru   …then fade)r   r   r*   r   r   r   test_laughter_recency_decaysr+   8   sG    
H
$'00006??KKK?v..r   c                  V    t        d      } dD ]  \  }}t        ||| d      |k(  rJ  y )Ng?energy))calmr/   )dryr0   )curiousr1   )sadr2      r   r   )r    tagstems      r    test_specific_tag_wins_over_toner7   A   s5    	#	B>	TCr2.$666>r   c                      t        d      } t        d      }t               }t        dd| d      dk(  sJ t        dd| d      dk(  sJ t        dd|d      dk(  sJ t        dd|d      dk(  sJ y )N333333r-   333333?excitedr3   warmamusedr4   )r!   r    neutrals      r   "test_bounce_softens_only_when_downr?   J   s    D!D	#	BkG	9dB76AAA(D"5???	9b"5BBB	9gr:iGGGr   c                      t        dd      } t        D ]>  }t        j                  |      }|
J | d       t	        ||| d      |k(  r6J | d        y )Ng333333      r.   pacez should map to a clipr   u0    must never soften — the meter earns its shout)r   r   r	   getr   )	deep_downr5   r6   s      r   2test_never_damp_list_holds_at_deep_down_late_nightrF   T   sn    D1I$>C5(=!>>Cy!4< 	EeCD	E< r   c                  V   t        d d t               d      J t        d d t        d      d      dk(  sJ t        d d t        d      d      dk(  sJ t        d d t        d      d      dk(  sJ t        d d t        d      d      d	k(  sJ t        d d t        d
      d      dk(  sJ y )Nr3   r:   r-   r=   g      ?r;   r   r9   r<   rA   tender)r   r   r   r   r   test_untagged_follows_tonerI   _   s    dIK4<<<dIS$92>(JJJdIS$92>)KKKdIC$8"=IIIdIT$:B?6IIIdIT$:B?8KKKr   c                  j    t        dd      } t        d d | d      dk(  sJ t        d d | d      dk(  sJ y )Nr9   rB      r/   r3   r<   r4   )tones    r   $test_late_night_flat_slow_is_softestrM   h   s@    Dt,DdD!,666dD"-777r   c                     t        d      t        _        t        ddddd      } d| _        t
        j                  j                  | d	      d
k(  sJ d| _        t
        j                  j                  | d	      dk(  sJ t                y )Nr9   r-   FBASEEXCWARM) r;   r<   )_plain_statesz[excited] hi)hirP   T)rU   rQ   )	r   r   r   r   	_tone_ttsr   	PocketTTS
_state_forr   )fakes    r   test_flag_gates_the_biasrZ   p   s~    #40Gv>@D DN**4@MQQQDN**4@NRRR
Hr   __main__test_zPASS     zFAIL  
/z passed)r
   N)'__doc__typesr   voicer   voice.prosodyr   r   voice.tts_pocketr   r   r	   r   strboolr   r$   r(   r+   r7   r?   rF   rI   rM   rZ   __name__	tracebacksortedglobalsitems
startswithfnsfailsfnprint	Exception	print_exclen
SystemExit)kvs   00r   <module>rw      s^   "  #  D D
&
 $)    D  Y  J*/7HEL8 z	 12
L2Aall76K12
LCE	"DF2;;-()  
Bs3x% #c(7
34
%Q
''Q
'' 
L  	"QJEF2;;-()I!	"s   8C/C/"C550D('D(