
    `gjq                    R   U d Z ddlmZ ddlZddlZddlZddlZddlmZm	Z	m
Z
 ddddddZddd	d	d	d
dddddddZ eh d      Z eh d      ZdZd+dZd,dZd,dZddlmZmZmZmZ  ej2                  e      Zd-dZd.dZd/d0dZda ej@                         Z! ej@                         Z"da#da$da%de&d<   da'de&d<    ejP                         Z)e)jU                          da+de&d<   da,de&d <   da-d!e&d"<   da.d#Z/d1d$Z0d2d%Z1	 	 	 	 	 d3	 	 	 	 	 	 	 	 	 	 	 	 	 d4d&Z2d5d6d'Z3d.d(Z4d1d)Z5d7d*Z6y)8u  Process-wide voice recording + TTS API for the TUI gateway.

Wraps ``tools.voice_mode`` (recording/transcription) and ``tools.tts_tool``
(text-to-speech) behind idempotent, stateful entry points that the gateway's
``voice.record``, ``voice.toggle``, and ``voice.tts`` JSON-RPC handlers can
call from a dedicated thread. The gateway imports this module lazily so that
missing optional audio deps (sounddevice, faster-whisper, numpy) surface as
an ``ImportError`` at call time, not at startup.

Two usage modes are exposed:

* **Push-to-talk** (``start_recording`` / ``stop_and_transcribe``) — single
  manually-bounded capture used when the caller drives the start/stop pair
  explicitly.
* **Continuous (VAD)** (``start_continuous`` / ``stop_continuous``) — mirrors
  the classic CLI voice mode: recording auto-stops on silence, transcribes,
  hands the result to a callback, and then auto-restarts for the next turn.
  Three consecutive no-speech cycles stop the loop and fire
  ``on_silent_limit`` so the UI can turn the mode off.
    )annotationsN)AnyCallableOptionalc-a-)ctrlcontrolaltoptionoptspaceentertabescape	backspacedelete)r   spcr   returnretr   r   escr   bsr   del>   cdlzc-br   c                    t        | t              sy| j                  d      }t        |t              sy|j                  d      S )a  Shape-safe ``cfg.voice.record_key`` lookup.

    ``load_config()`` deep-merges raw YAML and preserves scalar
    overrides, so a hand-edited ``voice: true`` / ``voice: cmd+b``
    leaves ``cfg["voice"]`` as a bool/str instead of a dict, and the
    naive ``.get("voice", {}).get("record_key")`` chain raises
    AttributeError before voice can even start (Copilot round-11 on
    #19835). Return ``None`` for malformed shapes so call sites can
    feed the result straight into the normalizer/formatter and get
    the documented default.
    Nvoice
record_key)
isinstancedictget)cfgr   s     C/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/voice.pyvoice_record_key_from_configr%   V   s;     c4 GGGEeT"99\""    c                   t        | t              st        S | j                         j	                         }|st        S |j                  d      D cg c]#  }|j                         s|j                         % }}|st        S t        |      dkD  rt        S t        |      dk(  rt        S |\  }}|dv rt        S t        j                  |      }|st        S t        |      dk(  r?|dk(  r|t        v rt        S |dk(  r!t        j                  dk(  r|t        v rt        S | | S t        j                  |      }|st        S | | S c c}w )u  Coerce ``voice.record_key`` into prompt_toolkit's ``c-x`` / ``a-x`` format.

    Mirrors the TUI parser contract (``ui-tui/src/lib/platform.ts``)
    so one config value binds the same shortcut in both runtimes:

    * non-string / empty / typo'd / bare-char / multi-modifier / reserved
      ``ctrl+c|d|l`` → documented default ``c-b``
    * single-char keys: ``ctrl+o`` → ``c-o``
    * named keys: ``ctrl+space`` → ``c-space`` (aliases collapse:
      ``ctrl+return`` → ``c-enter``)
    * ``super`` / ``win`` / ``windows`` → ``c-b`` (TUI-only modifiers —
      prompt_toolkit has no super mod; the CLI binding site is
      expected to warn when this fallback fires so users see the
      cross-runtime split, Copilot round-11 on #19835)
    +      >   winsuperwindowsr   r   darwin)r    str_DEFAULT_PT_KEYstriplowersplitlen_VOICE_MOD_ALIASESr"   _VOICE_RESERVED_CTRL_CHARSsysplatform_VOICE_RESERVED_ALT_CHARS_MAC_VOICE_NAMED_KEYS)rawloweredppartsmodifier_token	key_tokennormalized_modnameds           r$   -normalize_voice_record_key_for_prompt_toolkitrC   l   sG     c3iik!G 'c 2@1aggiQWWY@E@
 5zA~
 5zQ %NI 44'++N;N 9~T!i3M&M""d"(::"" !)--
 !!),EeW%%g As   E&Ec                   t        |       }|j                  d      rd|dd }}nP|j                  d      rd|dd }}n7d|v r2|j                  dd      \  }}|d	   j                         |dd z   dz   }ny
|s|j	                  d      S t        |      dk(  r||j                         z   S ||d	   j                         z   |dd z   S )aF  Render ``voice.record_key`` for ``/voice status`` in CLI-friendly form.

    Mirrors the TUI's ``formatVoiceRecordKey``: returns ``Ctrl+B`` /
    ``Alt+Space`` / ``Ctrl+Enter``. Malformed configs surface as the
    documented default so status never advertises a shortcut that
    won't bind (Copilot round-10 on #19835).
    r   zCtrl+r)   Nr   zAlt+r(   r*   r   zCtrl+B)rC   
startswithr3   upperrstripr4   )r;   
normalizedprefixkeymods        r$   "format_voice_record_key_for_statusrL      s     ?sCJT"z!"~			t	$jn	
	 ##C+SQ#ab')C/}}S!!
3x1}		##CFLLN"SW,,r&   )create_audio_recorderis_whisper_hallucinationplay_audio_filetranscribe_recordingc                    t         j                  j                  dd      j                         dk7  ry	 t	        d|  t
        j                  d       y# t        t        f$ r Y yw xY w)u^  Emit a debug breadcrumb when HERMES_VOICE_DEBUG=1.

    Goes to stderr so the TUI gateway wraps it as a gateway.stderr event,
    which createGatewayEventHandler shows as an Activity line — exactly
    what we need to diagnose "why didn't the loop auto-restart?" in the
    user's real terminal without shipping a separate debug RPC.

    Any OSError / BrokenPipeError is swallowed because this fires from
    background threads (silence callback, TTS daemon, beep) where a
    broken stderr pipe must not kill the whole gateway — the main
    command pipe (stdin+stdout) is what actually matters.
    HERMES_VOICE_DEBUG 1Nz[voice] T)fileflush)	osenvironr"   r1   printr7   stderrBrokenPipeErrorOSError)msgs    r$   _debugr^      s[     
zz~~*B/5573>SZZt<W% s   A A&%A&boolc                     	 ddl m}   |        j                  di       }t        |t              rt        |j                  dd            S 	 y# t        $ r Y yw xY w)z=CLI parity: voice.beep_enabled in config.yaml (default True).r   )load_configr   beep_enabledT)hermes_cli.configra   r"   r    r!   r_   	Exception)ra   	voice_cfgs     r$   _beeps_enabledrf      s_    1M%%gr2	i&	nd;<< '   s   AA 	AAc                    t               sy	 ddlm}  || |       y# t        $ r}t	        d|  d|        Y d}~yd}~ww xY w)uU  Audible cue matching cli.py's record/stop beeps.

    880 Hz single-beep on start (cli.py:_voice_start_recording line 7532),
    660 Hz double-beep on stop (cli.py:_voice_stop_and_transcribe line 7585).
    Best-effort — sounddevice failures are silently swallowed so the
    voice loop never breaks because a speaker was unavailable.
    Nr   )	play_beep	frequencycountzbeep zHz failed: )rf   tools.voice_moderh   rd   r^   )rj   rk   rh   es       r$   
_play_beeprn     sG     2.IU3 2ykQC0112s    	A=AFT_continuous_auto_restart_continuous_recorderOptional[Callable[[str], None]]_continuous_on_transcript_continuous_on_statusOptional[Callable[[], None]]_continuous_on_silent_limit   c                     t         5  t        t        t        dd      r
	 ddd       yt               } | j	                          | addd       y# 1 sw Y   yxY w)u   Begin capturing from the default input device (push-to-talk).

    Idempotent — calling again while a recording is in progress is a no-op.
    Nis_recordingF)_recorder_lock	_recordergetattrrM   start)recs    r$   start_recordingr~   5  sR     
  WY%N  $%			  s   AAAc                    t         5  t        } daddd        y| j                         }|sy	 t        |      }	 	 t        j                  j                  |      rt        j                  |       |j                  d      sy|j                  d      xs dj                         }|rt        |      ry|S # 1 sw Y   xY w# t        $ re}t
        j                  d|       Y d}~	 t        j                  j                  |      rt        j                  |       yy# t        $ r Y yw xY wd}~ww xY w# t        $ r Y w xY w# 	 t        j                  j                  |      rt        j                  |       w w # t        $ r Y w w xY wxY w)zStop the active push-to-talk recording, transcribe, return text.

    Returns ``None`` when no recording is active, when the microphone
    captured no speech, or when Whisper returned a known hallucination.
    Nzvoice transcription failed: %ssuccess
transcriptrS   )ry   rz   stoprP   rd   loggerwarningrW   pathisfileunlinkr"   r1   rN   )r}   wav_pathresultrm   texts        r$   stop_and_transcriber   D  sV    
 	 {xxzH
%h/
	ww~~h'		(# ::i JJ|$*113D+D1K?   7;	ww~~h'		(# ( 		  			ww~~h'		(# ( 		s{   	B5C 4D2 5B>	D/
D* E %4D 	D'&D'*D//E 2	D>=D>F4E97F9	FFFFc                   t         5  t        rt        d       	 ddd       yt        rt        d       	 ddd       yda|a| a|a|a|rdat        
t               a	|t        _        |t        _        t        }ddd       t        d| d| d	       t        d
d       	 j                  t               |r
	  |d       yy# 1 sw Y   LxY w# t         $ r]}t"        j%                  d|       t        dt'        |      j(                   d|        t         5  daddd        # 1 sw Y    xY wd}~ww xY w# t         $ r Y yw xY w)u  Start a VAD-driven continuous recording loop.

    The loop calls ``on_transcript(text)`` each time speech is detected and
    transcribed successfully. If ``auto_restart`` is True, it auto-restarts
    for the next turn and resets the no-speech counter for that loop. If
    ``auto_restart`` is False, the first silence-triggered transcription ends
    the loop and reports ``"idle"``; no-speech counts are retained across
    starts so a push-to-talk caller can still enforce the three-strikes guard.
    After ``_CONTINUOUS_NO_SPEECH_LIMIT`` consecutive silent cycles (no speech
    picked up at all) the loop stops itself and calls ``on_silent_limit`` so the
    UI can reflect "voice off". Returns False if a previous stop is still
    transcribing/cleaning up; otherwise returns True. Idempotent — calling while
    already active is a successful no-op.

    ``on_status`` is called with ``"listening"`` / ``"transcribing"`` /
    ``"idle"`` so the UI can show a live indicator.
    u*   start_continuous: already active — no-opNTu6   start_continuous: stop/transcribe in progress — busyFr   z#start_continuous: begin (threshold=z, duration=zs)p  r*   ri   on_silence_stopz(failed to start continuous recording: %sz#start_continuous: rec.start raised : 	listening)_continuous_lock_continuous_activer^   _continuous_stoppingro   rr   rs   ru   _continuous_no_speech_countrp   rM   _silence_threshold_silence_durationrn   r|   _continuous_on_silencerd   r   errortype__name__)on_transcript	on_statuson_silent_limitsilence_thresholdsilence_durationauto_restartr}   rm   s           r$   start_continuousr   q  sn   : 
 #?@# #  KL# # "#/ $1! )&5#*+''#8#: 2C/1A.")#, 
-.?-@L\K]]_` A&		"8	9 	k" ]# #B  ?C4T!W5E5E4FbLM 	'!&	'	'  		sX   CC:C C 9D8 C	D5A D0D$	D0$D-	)D00D58	EEc                j  	
 t         5  t        s
	 ddd       ydat        }t        t        t
        t        }| xr | 	|duadadada	sdaddd       `| rMrKr		  d       	 |j                         
	
fd}t        j                  |d	      j!                          y	 |j                          t         5  daddd       t#        d
d       r
	  d       yy# 1 sw Y   xY w# t        $ r Y w xY w# t        $ r`}t        j                  d|       	 |j                          n,# t        $ r }t        j                  d|       Y d}~nd}~ww xY wd
Y d}~d}~ww xY w# t        $ r }t        j                  d|       Y d}~d}~ww xY w# 1 sw Y   xY w# t        $ r Y yw xY w)u?  Stop the active continuous loop and release the microphone.

    Idempotent — calling while not active is a no-op. If ``force_transcribe`` is
    True, the recorder stops synchronously, then transcription/cleanup runs on a
    background thread before reporting ``"idle"``. Otherwise the buffer is
    discarded.
    NFr   transcribingzfailed to stop recorder: %szfailed to cancel recorder: %sc                 F   d } d}	 	r	 t        	      }|j                  d      r2|j                  d      xs dj                         }|rt        |      s|} t        j
                  j                  	      rt	        j                  	       	 | r		  |        r:t        5  | rdant        dz  at        t        k\  }|rdad d d        |r
r	          t        d	d
       t        5  dad d d        r
	  d       y y # t        j
                  j                  	      rt	        j                  	       w w xY w# t        $ r }t        j                  d|       Y d }~d }~ww xY w# t        $ r }t        j                  d|       Y d }~d }~ww xY w# 1 sw Y   xY w# t        $ r Y w xY w# 1 sw Y   xY w# t        $ r Y y w xY w# | r6	  |        n,# t        $ r }t        j                  d|       Y d }~nd }~ww xY wrWt        5  | rdant        dz  at        t        k\  }|rdad d d        n# 1 sw Y   nxY w|rr	          n# t        $ r Y nw xY wt        d	d
       t        5  dad d d        n# 1 sw Y   nxY wr	  d       w # t        $ r Y w w xY ww xY w)NFr   r   rS   z&failed to stop/transcribe recorder: %s!on_transcript callback raised: %sr   r*     r)   ri   idle)rP   r"   r1   rN   rW   r   r   r   rd   r   r   r   r   _CONTINUOUS_NO_SPEECH_LIMITrn   r   )
r   should_haltr   r   rm   r   r   r   track_no_speechr   s
        r$   _transcribe_and_cleanupz0stop_continuous.<locals>._transcribe_and_cleanup  s   ,0
#-!4%9(%CF%zz)4(.

<(@(FB'M'M'O#'0H0N15J!ww~~h7 "		( 3 "S)*5 '- 
D)>? ; ;q @ ;$?'B%C !, $/BC$?
D '?% / 1 A6) 5/4,5 !%f- !A  "ww~~h7 "		( 3  8  PNN#KQOOP  ) S"NN+NPQRRS
D 
D $- % $%5 5
  ) ! != "S)*5( S"NN+NPQRRS '- 
D)>? ; ;q @ ;$?'B%C !, $/BC$?
D 
D 
D '?% / 1#, % $% A6) 5/4,5 5 5 !%f-( ! ! !s7  D9 AC? 5D9 E%   FF 'F,5F8 ?7D66D9 9	E"EG E""G %	F.F		FF	F)(F),F58	GGJ GJ 	G=G83J 8G==J  H1(	J 1H:6J I
	J 
	IJ IJ ,I8/	J 8J=	J JJ 	JJ JJ T)targetdaemonr   r)   ri   r   )r   r   rp   rs   rr   ru   ro   r   r   rd   r   r   r   cancel	threadingThreadr|   rn   )force_transcriber}   r   rm   cancel_errorr   r   r   r   r   r   s         @@@@@r$   stop_continuousr     s    
 ,!, , #")	15/*?</?"$$(! $&*#*+'," n- 88:2! 2!h $;DIOOQC 

 
 %$%
 A&	f O, ,, !    <a@RJJL  RNN#BLQQR F  C>BBC% %  		s   C'5C'#C3 ,D ,E. FF& 'C03	C?>C?	E+E&"D32E&3	E<EE&EE&&E+.	F7FFF#&	F21F2c                 F    t         5  t        cddd       S # 1 sw Y   yxY w)z5Whether a continuous voice loop is currently running.N)r   r    r&   r$   is_continuous_activer   9  s    	 "!" " "s    c                 	   t        d       t        5  t        st        d       	 ddd       yt        } t        }t
        }t        }ddd        t        d       yr		  |d       | j                         }t        | dd      }t        d|d	| d
       t        dd       d}|r	 t        |      }t        |j                  d            }|j                  d      xs dj                         }	|j                  d      }
t        d| d|	d|
       |r|	rt        |	      s|	}	 t(        j*                  j-                  |      rt)        j.                  |       t        5  t        st        d       	 ddd       y|rdan	t0        dz  at0        t2        k\  }t0        }ddd       |rr		  ||       rJt        d d       t        5  dadaddd       r	  |        	 | j5                          |r
	  |d       yyt6        j9                         sat        d        t6        j;                  d!"       ddl}|j?                  d#       t        5  t        st        d$       	 ddd       y	 ddd       t@        r@t        d% d
       t        d&d       	 | jC                  tD        '       |r
	  |d*       yyt        d+       t        5  daddd       |r
	  |d       yy# 1 sw Y   xY w# t        $ r Y w xY w# t        $ rE}t         j#                  d|       t        dt%        |      j&                   d|        Y d}~#d}~ww xY w# t        $ r Y w xY w# 	 t(        j*                  j-                  |      rt)        j.                  |       w w # t        $ r Y w w xY wxY w# 1 sw Y   xY w# t        $ r!}t         j#                  d|       Y d}~*d}~ww xY w# 1 sw Y   xY w# t        $ r Y w xY w# t        $ r Y w xY w# t        $ r Y yw xY w# 1 sw Y   xY w# t        $ r}}t         jG                  d(|       t        d)t%        |      j&                   d|        t        5  daddd       n# 1 sw Y   nxY w|r	  |d       n# t        $ r Y nw xY wY d}~yd}~ww xY w# t        $ r Y yw xY w# 1 sw Y   xY w# t        $ r Y yw xY w),u  AudioRecorder silence callback — runs in a daemon thread.

    Stops the current capture, transcribes, delivers the text via
    ``on_transcript``, and — if the loop is still active — starts the
    next capture. Three consecutive silent cycles end the loop.
    z_continuous_on_silence: firedu/   _continuous_on_silence: loop inactive — abortNu-   _continuous_on_silence: no recorder — abortr   	_peak_rmsz$_continuous_on_silence: rec.stop -> z (peak_rms=)r   r)   ri   r   r   rS   r   z._continuous_on_silence: transcribe -> success=z text=z err=z#continuous transcription failed: %sz*_continuous_on_silence: transcribe raised r   u@   _continuous_on_silence: stopped during transcribe — no restartr   r*   r   z_continuous_on_silence: u    silent cycles — haltingFr   z1_continuous_on_silence: waiting for TTS to finish<   )timeout333333?z5_continuous_on_silence: stopped while waiting for TTSz3_continuous_on_silence: restarting loop (no_speech=r   r   z*failed to restart continuous recording: %sz'_continuous_on_silence: restart raised r   z9_continuous_on_silence: auto_restart=False, stopping loop)$r^   r   r   rp   rr   rs   ru   rd   r   r{   rn   rP   r_   r"   r1   rN   r   r   r   r   rW   r   r   r   r   r   r   _tts_playingis_setwaittimesleepro   r|   r   r   )r}   r   r   r   r   peak_rmsr   r   r   r   errrm   r   	no_speech_times                  r$   r   r   ?  s    *+	 6!DE6 6 #1)	56 {>?	n% xxzH sK,H

.xl+hZqQ A& $J	)(3F
 6::i01GJJ|,299;D**W%C@	 JxuSG- 4(@(F!

77>>(+IIh' 
 
0!UV	
0 
0
 *+''1,'15PP/	
0 m	C*% ))4NOP 	,!&*+'	, !	JJL &! 	  BC"%C  	%NO	 	%	
  DYKqQRS*	II&<I= +&  	JK 	'!&	'&! a6 6   		D  	YNN@!D?Q@P@P?QQSTUSVWXX	Y  77>>(+IIh' , 
0 
0  	CNN>BB	C
	, 	,    		
   	 	  
	LLEqI<T!W=M=M<NbQRPSTU! +%*"+ + +f%  
	  
	' 	'
  s  K#K#K0 (A>L  '4M !N+=!N++N8 
O%O2 "P 5P P!P. 0R7 SS #K-0	K=<K= 	M	:M	M! 	MM! 	MM!N(#4NN(	N%"N($N%%N(+N58	O"OO"%O/2	O?>O?	PP	PP!P+.	R47A R/7R:	R/R		R/RR/	R'$R/&R''R//R47	SSS	SSc                   | r| j                         syddl}ddl}ddl}d}t        5  t
        r.t        (t        t        dd      r	 t        j                          d}ddd       t        j                          t        d| d       	 dd	lm} t#        |       d
kD  r| dd
 n| }|j%                  dd|      }|j%                  dd|      }|j%                  dd|      }|j%                  dd|      }|j%                  dd|      }|j%                  dd|      }|j%                  dd||j&                        }|j%                  dd||j&                        }|j%                  dd|      }|j%                  dd|      }|j                         }|sv	 t        j)                          t        d       |rS|j+                  d       t        5  t
        r,t        &	 t        j-                  t.               t        d       ddd       yyt1        j2                  t0        j4                  j7                  |j9                         d      d        t0        j4                  j7                  |j9                         dd!|j;                  d"       d#      }t        d$t#        |       d%|         |||&       t0        j4                  j=                  |      rt0        j4                  j?                  |      dkD  rt        d'| d(t0        j4                  j?                  |       d)       tA        |       	 t1        jB                  |       |jE                  d*d+      d   d,z   }	t0        j4                  j=                  |	      rt1        jB                  |	       nt        d-|        t        j)                          t        d       |rS|j+                  d       t        5  t
        r,t        &	 t        j-                  t.               t        d       ddd       yy# t        $ r!}t        j                  d|       Y d}~d}~ww xY w# 1 sw Y   xY w# t        $ r!}t        j                  d|       Y d}~sd}~ww xY w# 1 sw Y   yxY w# tF        $ r Y w xY w# t        $ rE}t        j                  d.|       t        d/tI        |      jJ                   d0|        Y d}~@d}~ww xY w# t        $ r!}t        j                  d|       Y d}~d}~ww xY w# 1 sw Y   yxY w# t        j)                          t        d       |r|j+                  d       t        5  t
        rYt        S	 t        j-                  t.               t        d       n,# t        $ r }t        j                  d|       Y d}~nd}~ww xY wddd       w # 1 sw Y   w xY ww xY w)1u  Synthesize ``text`` with the configured TTS provider and play it.

    Mirrors cli.py:_voice_speak_response exactly — same markdown strip
    pipeline, same 4000-char cap, same explicit mp3 output path, same
    MP3-over-OGG playback choice (afplay misbehaves on OGG), same cleanup
    of both extensions. Keeping these in sync means a voice-mode TTS
    session in the TUI sounds identical to one in the classic CLI.

    While playback is in flight the module-level _tts_playing Event is
    cleared so the continuous-recording loop knows to wait before
    re-arming the mic (otherwise the agent's spoken reply feedback-loops
    through the microphone and the agent ends up replying to itself).
    Nr   Frx   Tz$failed to pause recorder for TTS: %sz(speak_text: TTS begin (paused_recording=r   )text_to_speech_tooli  z```[\s\S]*?``` z\[([^\]]+)\]\([^)]+\)z\1zhttps?://\S+rS   z\*\*(.+?)\*\*z	\*(.+?)\*z`(.+?)`z^#+\s*)flagsz^\s*[-*]\s+z---+z\n{3,}z

zspeak_text: TTS doner   r   z'speak_text: recording resumed after TTSz'failed to resume recorder after TTS: %shermes_voice)exist_oktts_z%Y%m%d_%H%M%Sz.mp3zspeak_text: synthesizing z
 chars -> )r   output_pathzspeak_text: playing z (z bytes).r*   z.oggz*speak_text: TTS tool produced no audio at zVoice TTS playback failed: %szspeak_text raised r   )&r1   retempfiler   r   r   rp   r{   r   rd   r   r   r   clearr^   tools.tts_toolr   r4   sub	MULTILINEsetr   r|   r   rW   makedirsr   join
gettempdirstrftimer   getsizerO   r   rsplitr\   r   r   )
r   r   r   r   paused_recordingrm   r   tts_textmp3_pathogg_paths
             r$   
speak_textr     s    tzz| 	 
J$0,neDJ$++-#' 
J 
56F5GqIJA6"%d)d"24;66+S(;662E8D66/2x866*E8<66,x866*eX666)R6F66."hbll6K66'2x066)VX6>>#> 	%&
 JJsO! 
%*>*J,22,B 3  HI
 
 A 	BGGLL!4!4!6GRVW77<<!4==12$7
 	*3x=/H:NOx@77>>(#(AA(E)(2bggooh6O5PPWXYH%		(##??3215>77>>(+IIh' ?zJK
 	%&
 JJsO! 
%*>*J,22,B 3  HI
 
 w  JEqIIJ
J 
JZ % Eq 
 
!    =6:#DG$4$4#5Rs;<<=& % Eq 
 
 	%&
 JJsO! 
%*>*J,22,B 3  HI$ Eq 
 
 
 s(  P9P	DR Q3%QD$R 'A!Q? R T%S	P6P1+P91P66P99Q	Q0Q+%Q3+Q00Q33Q<?	RR 
RR 	S:ST ST 	T	(T>TT		TT9WV:%VV:	V.	V)	$V:)V.	.V:1	W:W?W)r#   r   r   r   )r;   r   r   r/   )r]   r/   r   None)r   r_   )r*   )rj   intrk   r   r   r   )r   r   )r   zOptional[str])NN   g      @T)r   zCallable[[str], None]r   rq   r   rt   r   r   r   floatr   r_   r   r_   )F)r   r_   r   r   )r   r/   r   r   )7__doc__
__future__r   loggingrW   r7   r   typingr   r   r   r5   r:   	frozensetr6   r9   r0   r%   rC   rL   rl   rM   rN   rO   rP   	getLoggerr   r   r^   rf   rn   rz   Lockry   r   r   r   ro   __annotations__rp   Eventr   r   rr   rs   ru   r   r   r~   r   r   r   r   r   r   r   r&   r$   <module>r      s  * #  	 
  * *   
 & '7  !*/ : #,J&Z->  
		8	$*
2$ 	! "9>>#   !% $ %  c   y     =A : A9= 6 =<@ 9 @  '^ 2648 !K(K.K 2K 	K
 K K 
K\wt"_Jjr&   