
    Sj#                    t   d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
mZ ddl
mZ  G d d      Zde_         eddd	d
 i      Z G d d      ZddZd Zd Zd Zedk(  rY e e       j3                               D ]2  \  ZZej9                  d      s ee      s! e         ede        4  ed       yy)a  Inactivity-watchdog test for ClaudeBrain.reply().

A hung SDK response stream must NOT block reply() forever (which would hold
the brain lock and freeze the HUD spinner). With a fake client whose stream
goes silent, reply() should trip _BRAIN_INACTIVITY_S, yield a recovery line,
release the lock, and leave the turn marked stale for the next drain. A
normal stream must be unaffected.

Run:  .venv/bin/python -m pytest test_brain_watchdog.py -q
    )annotationsN)ClaudeBrainc                      e Zd ZdZdZ ddZy)_FakeStreamEventzCMimics the SDK StreamEvent shape reply() reads (type name matters).StreamEventc                    dd|dd| _         y )Ncontent_block_delta
text_deltatypetextr   delta)eventselfr   s     A/Users/ahmed/devFolder/Ultron/claude-voice/test_brain_watchdog.py__init__z_FakeStreamEvent.__init__   s    3(4dCE
    Nr   strreturnNone)__name__
__module____qualname____doc__r    r   r   r   r      s    MHEr   r   r   r   r   c                (    t        | ddd|dd      S )Nr   r	   r
   r   r   )setattrr   s     r   <lambda>r!      s    7g&'6	8$9r   c                  *    e Zd ZddZddZddZd Zy)	
FakeClientc                .    || _         d| _        d| _        y )Nr   z	sess-test)mode
interrupts
session_id)r   r%   s     r   r   zFakeClient.__init__&   s    	%r   c                   K   y w)Nr   r   s     r   queryzFakeClient.query+   s	     s   c                6   K   | xj                   dz  c_         y w)N   )r&   r   s    r   	interruptzFakeClient.interrupt.   s     1s   c                  K   | j                   dk(  rt        d       t        d       y t        j                  d       d {    t        d       y 7 w)NnormalzRight away, sir. zDone.<   ztoo late)r%   r   asynciosleepr,   s    r   receive_responsezFakeClient.receive_response1   sN     99 122g&&mmB*%% 	 s   AAAAN)r%   r   r   r   r   )r   r   )r   r   r   r   r)   r-   r3   r   r   r   r#   r#   %   s    &
&r   r#   c                    t        j                  t               }t        |       |_        t	        j
                         |_        d|_        d|_        |S )NFT)	r   __new__r#   _clientr1   Lock_lock
_turn_open_session_saved)r%   bs     r   _brainr<   ;   s?    K(A4 AIllnAGALAHr   c                 >    d } t        j                   |               y )Nc            
       K   t        d      } | j                  d      2 cg c3 d {   }|7 6 nc c}w }}d}|j                  } ||      }|j                  } |       }d}||k7  }	|	st	        j
                  d|	fd||f      t	        j                  |      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |      dz  }
dd	|
iz  }t        t	        j                  |            d x}x}x}x}x}x}	}| j                  }d
}||u }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |       rt	        j                  |       ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}y w)Nr/   hello )!=)z%(py10)s
{%(py10)s = %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py3)s
{%(py3)s = %(py1)s.join
}(%(py4)s)
}.strip
}()
} != %(py13)sout)py1py3py4py6py8py10py13zassert %(py15)spy15Fisz2%(py2)s
{%(py2)s = %(py0)s._turn_open
} is %(py5)sr;   py0py2py5zassert %(py7)spy7)r<   replyjoinstrip
@pytest_ar_call_reprcompare	_saferepr@py_builtinslocals_should_repr_global_nameAssertionError_format_explanationr9   )r;   srB   @py_assert0@py_assert2@py_assert5@py_assert7@py_assert9@py_assert12@py_assert11@py_format14@py_format16@py_assert1@py_assert4@py_assert3@py_format6@py_format8s                    r   goz(test_normal_stream_completes.<locals>.goE   se    8 ! 0111q1111)rww)ws|)|!!)!#)r)#r))))#r)))r)))w))))))s)))s)))|)))!)))#)))r))))))))||$u$|u$$$$|u$$$$$$q$$$q$$$|$$$u$$$$$$$s'   H?31/
1313HH?r1   runrm   s    r   test_normal_stream_completesrq   D   s    %
 KKr   c                 >    d } t        j                   |               y )Nc                   K   t         j                  } dt         _        	 t        d      }t        j                         j                         }|j                  d      2 cg c3 d {   }|7 6 nc c}w }}t        j                         j                         |z
  }d}||k  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d	      d
z   d|iz  }	t        t        j                  |	            d x}}d |D        }t!        |      }
|
st        j                  d      dz   dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |
      dz  }t        t        j                  |            d x}}
|j"                  }d}||u }
|
st        j                  d|
fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }t        t        j                  |            d x}x}
}|j$                  }|j&                  }
 |
       }| }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      t        j                  |      dz  }t        t        j                  |            d x}x}
x}}| t         _        y # | t         _        w xY ww)Ng333333?hangz
do a thingg      @)<)z%(py0)s < %(py3)stook)rO   rD   z*watchdog must not wait for the hung streamz
>assert %(py5)srQ   c              3  @   K   | ]  }d |j                         v   yw)hungN)lower).0r^   s     r   	<genexpr>z=test_hung_stream_recovers_fast.<locals>.go.<locals>.<genexpr>W   s     8Cqv*Cs   zshould speak a recovery linez.
>assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}any)rO   rP   rE   TrK   rM   r;   rN   z+turn left stale so the next reply drains itz
>assert %(py7)srR   z`assert not %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s._lock
}.locked
}()
})rO   rP   rE   rF   )llm_BRAIN_INACTIVITY_Sr<   r1   get_event_looptimerS   rV   rW   rY   rZ   r[   rX   _format_assertmsgr\   r]   r|   r9   r8   locked)oldr;   t0r^   rB   rv   r`   rh   @py_format4rk   rj   @py_format5ri   rl   ra   rb   s                   r   rm   z*test_hung_stream_recovers_fast.<locals>.goN   s    %%"%	*vA'')..0B$%GGL$9::q1:::C:))+002R7DK4#:KKK4#KKKKKK4KKK4KKK#KKKKKKKKKK8C8 /388 /8 /.../ /(./ /..  / /%.Y  / /%.Y 9 / /%.Y 9 / / /../ /<< >4 ><4' >,=,=><4 > >7=v> >%=%=  > >4=I  > >4=I   > >4=I $( > >,=,==> > >*=*=> > ww'w~~'~'''''''''''q'''q'''w'''~''''''''''&)C#cC#sN   O$A O A1!A/%A-
&A/)A1-A//A10MO O$O!!O$rn   rp   s    r   test_hung_stream_recovers_fastr   M   s    *" KKr   c                    t         j                  } t        | t              }|s'ddt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d x} }y )Nz^assert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s._BRAIN_INACTIVITY_S
}, %(py4)s)
}
isinstancer}   float)rO   rC   rD   rE   rF   )r}   r~   r   r   rY   rZ   rV   r[   rX   r\   r]   )r`   ra   @py_format7s      r   %test_disabled_watchdog_setting_parsesr   b   s     --5:-u55555555:555:555555c555c555-555555u555u5555555555r   __main__test_z  ok  z	all green)r%   r   r   r   )r   
__future__r   builtinsrY   _pytest.assertion.rewrite	assertionrewriterV   r1   	voice.llmr}   r   r   r   r   r   r#   r<   rq   r   r   r   sortedglobalsitemsnamefn
startswithcallableprintr   r   r   <module>r      s   	 #     !E E !.  =" 9': ;& &,*6 z79??,-b??7#DF4&/" . 
+ r   