§
    øžyj&å  ã            
      óZ  — 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Zddlm	Z	 ddl
mZmZmZmZ  ej        e¦  «        ZdZdZdZd	Zd
Zd
Z G d„ de¦  «        Zdddddddeddœ	Zded<   dZ eh d£¦  «        Zdydzd„Zd{d„Z d|d„Z!da"d}d!„Z#d{d"„Z$d~d#„Z%dd&„Z&d}d'„Z'd€d)„Z(d�d+„Z)d‚d-„Z*dƒd„d/„Z+	 dƒddd0œd…d3„Z,d{d4„Z-dƒd†d6„Z.d|d7„Z/d‡d9„Z0d:„ Z1d{d;„Z2dˆd=„Z3d‰d?„Z4dŠd@„Z5d‹dB„Z6d‰dC„Z7 G dD„ dE¦  «        Z8dŒdG„Z9 G dH„ dIe8¦  «        Z:dJZ;dKZ<d�dM„Z=dƒdŽdP„Z> G dQ„ dRe8¦  «        Z? G dS„ dTe8¦  «        Z@d�dU„ZAd{dV„ZBd{dW„ZCdƒd�dX„ZD G dY„ dZ¦  «        ZEdaFd[ed\<   daGd]ed^<   daH ejI        ¦   «         ZJd�d_„ZKdƒd‘da„ZLd’dc„ZMd“de„ZNdddfœd”dm„ZOd•dn„ZPd•do„ZQd•dp„ZRd•dq„ZSd{dr„ZTd{ds„ZUdƒd�dt„ZVd–dv„ZWd•dw„ZXd~dx„ZYdS )—u£  Wake-word ("Hey Hermes") detection â€” hands-free session trigger.

A lightweight, always-on hotword listener that fires a callback when a wake
phrase is spoken â€” the "Hey Siri" / "Alexa" pattern. Shared by the CLI, TUI, and
desktop GUI (one of them owns it, gated by ``wake_surface_enabled``): say the
wake word, Hermes opens a fresh session and captures voice via the existing
pipeline, then answers.

Three engines, all fully on-device (no audio leaves the machine for detection):

* **openwakeword** (default, free, no API key) â€” loads an ONNX model. Defaults
  to the bundled "hey hermes" model (``tools/wakewords/``) so the wake word
  works out of the box; or point ``wake_word.openwakeword.model`` at a built-in
  name (``hey_jarvis``, ``alexa``, â€¦) or a custom ``.onnx`` for another phrase.
* **sherpa** (free, no API key, open vocabulary) â€” sherpa-onnx keyword
  spotting. Detects ANY typed phrase with no training: set
  ``wake_word.phrase`` and the phrase is tokenized at runtime against a small
  streaming zipformer model (~13 MB English model, one-time download).
* **porcupine** (premium) â€” Picovoice's engine. Needs ``PORCUPINE_ACCESS_KEY``;
  supports built-in keywords and custom ``.ppn`` files from the Picovoice
  Console.

Audio capture reuses the same 16 kHz mono int16 ``sounddevice`` path as voice
mode. The detector runs on its own daemon thread; callers ``pause()`` it while a
voice turn holds the microphone and ``resume()`` it once the system is idle
again (two input streams on one device is unreliable cross-platform).

Nothing here mutates agent context or the prompt cache â€” on wake we hand a plain
string to the caller, exactly like a voice transcript.
é    )ÚannotationsN)ÚPath)ÚAnyÚCallableÚDictÚOptionali€>  ç       @g      @é   é
   c                  ó   — e Zd ZdZdS )ÚWakeWordInUsezCRaised when another surface or process owns the wake-word listener.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    ú5/home/ragecks/.hermes/hermes-agent/tools/wake_word.pyr   r   C   s   € € € € € ØMÐMÐMÐMr   r   FÚautoÚopenwakewordú
hey hermesg333333ã?T)	ÚenabledÚsurfaceÚinput_deviceÚcaptureÚproviderÚphraseÚsensitivityÚconfirmation_framesÚstart_new_sessionúDict[str, Any]Ú	_DEFAULTSÚ
hey_hermes>   Ú Úhermesr#   r   ÚonnxÚ	frameworkÚstrÚreturnc                ó  — t          | ¦  «                             ¦   «                              ¦   «         dk    rdnd}t          j                             t          j                             t          ¦  «        dt          › d|› �¦  «        S )zGPath to the shipped hey_hermes model (.onnx/.tflite) for ``framework``.Útfliter&   Ú	wakewordsú.)	r(   ÚstripÚlowerÚosÚpathÚjoinÚdirnameÚ__file__Ú_BUNDLED_MODEL_NAME)r'   Úexts     r   Ú_bundled_wakeword_pathr7   a   si   € å˜)‘n”n×*Ò*Ñ,Ô,×2Ò2Ñ4Ô4¸Ò@Ð@ˆ(ˆ(Àf€CÝŒ7�<Š<�œŸš­Ñ1Ô1°;ÕCVÐ@^Ð@^ÐY\Ð@^Ð@^Ñ_Ô_Ð_r   Úboolc                 óZ   — dd l } t          j         dk    o|                      ¦   «         dk    S )Nr   ÚdarwinÚarm64)ÚplatformÚsysÚmachine)r<   s    r   Ú_is_macos_arm64r?   g   s.   € Ø€O€O€OåŒ<˜8Ò#ÐE¨×(8Ò(8Ñ(:Ô(:¸gÒ(EÐEr   c                 ó&   — t          ¦   «         rdndS )uø  The openWakeWord backend to use on this platform.

    openWakeWord's ONNX backend produces near-zero scores on macOS ARM64 â€” its
    shared *embedding* model is the broken stage (the melspectrogram front-end
    and the wake classifier both match tflite exactly). The detector arms, the
    microphone works, and no phrase can ever cross the threshold. Prefer the
    tflite backend there; ONNX stays the default everywhere else.

    Upstream: https://github.com/dscripka/openWakeWord/issues/336
    r+   r&   )r?   r   r   r   Údefault_inference_frameworkrA   m   s   € õ 'Ñ(Ô(Ð4ˆ8ˆ8¨fÐ4r   Úcfgc                ó¦  — t          |                      d¦  «        t          ¦  «        r|                      d¦  «        ni }t          |                     d¦  «        pd¦  «                             ¦   «                              ¦   «         }|st          ¦   «         S |dk    r3t          ¦   «         r%t          sdat           
                    d¦  «         dS |S )u  Resolve the effective openWakeWord backend from config.

    Honors an explicit ``openwakeword.inference_framework`` â€” EXCEPT the one
    combination that is provably dead: an explicit ``onnx`` on macOS ARM64,
    where ONNX's embedding model never lets a phrase cross threshold (upstream
    #336). Existing macOS users who pinned ``onnx`` before the tflite fix landed
    would otherwise keep a wake word that arms but never fires. Coerce that one
    case to tflite (with a one-time warning) instead of silently shipping a dead
    ear. Every other explicit value is respected as-is; empty falls back to the
    platform default.
    r   Úinference_frameworkr$   r&   Tué   wake: openwakeword.inference_framework='onnx' is set but ONNX's embedding model never fires on macOS ARM64 (openWakeWord #336) â€” using tflite instead. Set inference_framework to '' (auto) or 'tflite' in config.yaml to silence this.r+   )Ú
isinstanceÚgetÚdictr(   r.   r/   rA   r?   Ú_warned_onnx_coercedÚloggerÚwarning)rB   Úsubr'   s      r   Úresolve_inference_frameworkrL   ~   sÍ   € õ &0°·²¸Ñ0GÔ0GÍÑ%NÔ%NÐ
Vˆ#�'Š'�.Ñ
!Ô
!Ð
!ÐTV€CÝ�C—G’GÐ1Ñ2Ô2Ð8°bÑ9Ô9×?Ò?ÑAÔA×GÒGÑIÔI€Iàð -Ý*Ñ,Ô,Ð,à�FÒÐ�Ñ0Ô0ÐÝ#ð 	Ø#'Ð Ý�NŠNð;ñô ð ð ˆxàÐr   c                 ó8  — 	 ddl } dS # t          $ r Y nw xY w	 ddlm} n# t          $ r Y dS w xY wddl}|                     d¦  «        }g |_        t          j         	                    d|¦  «         |t          j        d<   t                               d¦  «         dS )	us  Make ``import tflite_runtime.interpreter`` resolve, returning success.

    openWakeWord hardcodes that import but only declares ``tflite-runtime`` for
    ``platform_system == "Linux"``; on macOS the equivalent wheel is
    ``ai-edge-litert``. Alias the module so the upstream import succeeds. The
    alias is process-local â€” nothing is written to site-packages.
    r   NT)ÚinterpreterFÚtflite_runtimeztflite_runtime.interpreterz3wake word: bridged tflite_runtime -> ai_edge_litert)Útflite_runtime.interpreterÚImportErrorÚai_edge_litertrN   ÚtypesÚ
ModuleTypeÚ__path__r=   ÚmodulesÚ
setdefaultrI   Údebug)rO   Ú_litertrS   Úpkgs       r   Úensure_tflite_runtimer[   ¡   sØ   € ðØ)Ð)Ð)Ð)àˆtøÝð ð ð ØˆðøøøðØ9Ð9Ð9Ð9Ð9Ð9Ð9øÝð ð ð Øˆuˆuðøøøð €L€L€Là
×
Ò
Ð+Ñ
,Ô
,€CØ€C„LÝ„K×ÒÐ+¨SÑ1Ô1Ð1Ø07…C„KÐ,Ñ-Ý
‡L‚LÐFÑGÔGÐGØˆ4s   ‚ ˆ
”™   
.­.c                 ó¢   — 	 ddl m}   | ¦   «                              d¦  «        }n# t          $ r d}Y nw xY wt	          |t
          ¦  «        r|ni S )zAReturn the ``wake_word`` config section, shape-guarded to a dict.r   )Úload_configÚ	wake_wordN)Úhermes_cli.configr]   rF   Ú	ExceptionrE   rG   )r]   rB   s     r   Úload_wake_word_configra   ¿   sq   € ðØ1Ð1Ð1Ð1Ð1Ð1àˆk‰mŒm×Ò Ñ,Ô,ˆˆøÝð ð ð Øˆˆˆðøøøå˜S¥$Ñ'Ô'Ð/ˆ3ˆ3¨RÐ/s   ‚#& ¦5´5Úkeyr   c                óš   — |                       |t                                |¦  «        ¦  «        }|€t                                |¦  «        n|S ©N)rF   r"   )rB   rb   Úvals      r   Ú_getrf   Ê   s<   € Ø
�'Š'�#•y—}’} SÑ)Ô)Ñ
*Ô
*€CØ!$ �9�=Š=˜ÑÔÐ°#Ð5r   c                óˆ   — t          t          | d¦  «        ¦  «                             ¦   «                              ¦   «         pdS )Nr   r   )r(   rf   r.   r/   ©rB   s    r   Ú	_providerri   Ï   s7   € Ý�t�C˜Ñ$Ô$Ñ%Ô%×+Ò+Ñ-Ô-×3Ò3Ñ5Ô5ÐG¸ÐGr   úint | str | Nonec                óÌ   — t          | d¦  «        }|�t          |t          ¦  «        rdS t          |t          ¦  «        r|S t	          |¦  «                             ¦   «         }|pdS )zBConfigured PortAudio input selector, preserving indices and names.r   N)rf   rE   r8   Úintr(   r.   )rB   ÚrawÚvalues      r   Ú_input_devicero   Ó   s_   € å
ˆs�NÑ
#Ô
#€CØ
€{•j ¥dÑ+Ô+€{ØˆtÝ�#•sÑÔð Øˆ
Ý�‰HŒH�NŠNÑÔ€EØˆ=�DÐr   Úfloatc                óâ   — t          | d¦  «        }	 t          |¦  «        }n1# t          t          f$ r t          t          d         ¦  «        }Y nw xY wt          t          |d¦  «        d¦  «        S )Nr   ç        ç      ð?)rf   rp   Ú	TypeErrorÚ
ValueErrorr"   ÚminÚmax)rB   rm   Úss      r   Ú_sensitivityry   Þ   sq   € Ý
ˆs�MÑ
"Ô
"€Cð,Ý�#‰JŒJˆˆøÝ•zÐ"ð ,ð ,ð ,Ý•)˜MÔ*Ñ+Ô+ˆˆˆð,øøøå�s�1�c‰{Œ{˜CÑ Ô Ð s   ’" ¢+AÁArl   c                ó¼   — t          | d¦  «        }	 t          |¦  «        }n# t          t          f$ r
 t          }Y nw xY wt          t          |d¦  «        d¦  «        S )zöHow many consecutive over-threshold frames are required to fire.

    ``1`` restores the old single-frame behaviour; higher values reject
    ambient-speech blips at the cost of a few tens of ms of extra latency.
    Clamped to a sane 1..10.
    r   é   r   )rf   rl   rt   ru   Ú_DEFAULT_CONFIRMATION_FRAMESrv   rw   )rB   rm   Úns      r   Ú_confirmation_framesr~   ç   sg   € õ ˆsÐ)Ñ
*Ô
*€Cð)Ý�‰HŒHˆˆøÝ•zÐ"ð )ð )ð )Ý(ˆˆˆð)øøøå�s�1�a‰yŒy˜"ÑÔÐs   ’" ¢=¼=úOptional[Dict[str, Any]]c                ód   — | �| nt          ¦   «         } t          t          | d¦  «        ¦  «        pdS )zHHuman-facing wake phrase label (purely cosmetic; engine keys detection).Nr   r   )ra   r(   rf   rh   s    r   Úwake_phraser�   ö   s3   € à�ˆ#ˆ#Õ&;Ñ&=Ô&=€CÝ�t�C˜Ñ"Ô"Ñ#Ô#Ð3 |Ð3r   )Úprefer_clientÚforce_localr‚   rƒ   c               óü   — | �| nt          ¦   «         } |rdS t          t          | d¦  «        pd¦  «                             ¦   «                              ¦   «         }|dv rdS |dk    rdS t          ¦   «         rdS |rdS dS )zþReturn ``local`` or ``client`` capture mode for this arm.

    ``prefer_client`` is set by remote desktop (Mac mic, headless backend).
    ``force_local`` keeps CLI/TUI on the process mic. Config ``capture`` is
    ``auto`` | ``local`` | ``client``.
    NÚlocalr   r   )ÚclientÚremoteÚexternalr†   )ra   r(   rf   r.   r/   Ú_local_input_device_ready)rB   r‚   rƒ   rm   s       r   Úresolve_capture_moderŠ   ü   s¡   € ð �ˆ#ˆ#Õ&;Ñ&=Ô&=€CØð ØˆwÝ
�d�3˜	Ñ"Ô"Ð, fÑ
-Ô
-×
3Ò
3Ñ
5Ô
5×
;Ò
;Ñ
=Ô
=€CØ
Ð.Ð.Ð.ØˆxØ
ˆg‚~€~Øˆwõ
 !Ñ"Ô"ð ØˆwØð Øˆxð ˆ7r   c                 óÐ  — 	 t          ¦   «         \  } }n# t          t          f$ r Y dS w xY w	 |                      ¦   «         }n# t          $ r Y dS w xY wt          |t          ¦  «        r(t          |                     d¦  «        pd¦  «        dk    S 	 |D ]Y}t          |t          ¦  «        r|                     d¦  «        nd}|€t          |dd¦  «        }t          |pd¦  «        dk    r dS ŒZn# t          $ r Y dS w xY w	 |                      dd¦  «        }t          |t          ¦  «        r|                     d¦  «        nd}t          |pd¦  «        dk    S # t          $ r Y dS w xY w)zGTrue when PortAudio is importable and at least one input device exists.FÚmax_input_channelsr   NTÚinput)
Ú_import_audiorQ   ÚOSErrorÚquery_devicesr`   rE   rG   rl   rF   Úgetattr)ÚsdÚ_ÚdevicesÚdevÚchannelsÚinfos         r   r‰   r‰     sÇ  € ðÝ‘”‰ˆˆAˆAøÝ�Ð!ð ð ð ØˆuˆuðøøøðØ×"Ò"Ñ$Ô$ˆˆøÝð ð ð Øˆuˆuðøøøå�'�4Ñ Ô ð ?Ý�7—;’;Ð3Ñ4Ô4Ð9¸Ñ:Ô:¸QÒ>Ð>ðØð 	ð 	ˆCÝ8BÀ3ÍÑ8MÔ8MÐW�s—w’wÐ3Ñ4Ô4Ð4ÐSWˆHØÐÝ" 3Ð(<¸aÑ@Ô@�Ý�8�=˜qÑ!Ô! AÒ%Ð%Ø�t�tð &ð		øõ ð ð ð ØˆuˆuðøøøðØ×Ò  gÑ.Ô.ˆÝ5?ÀÅdÑ5KÔ5KÐR�4—8’8Ð0Ñ1Ô1Ð1ÐQRˆÝ�8�=˜qÑ!Ô! AÒ%Ð%øÝð ð ð ØˆuˆuðøøøsI   ‚ ”)¨)­A Á
AÁAÂAC. Ã,C. Ã.
C<Ã;C<Ä AE Å
E%Å$E%r   c                ó:  — |�|nt          ¦   «         }|                     d¦  «        sdS t          t          |d¦  «        ¦  «                             ¦   «                              ¦   «         pd}|dk    p)||                      ¦   «                              ¦   «         k    S )a/  Should ``surface`` (``cli`` / ``tui`` / ``gui``) host the listener?

    True when the wake word is enabled and the configured ``surface`` is either
    ``auto`` or this exact surface.  ``auto`` makes a surface eligible; the
    process/machine ownership lock still permits only the first claimant.
    Nr   Fr   r   )ra   rF   r(   rf   r.   r/   )r   rB   Úwants      r   Úwake_surface_enabledrš   <  s�   € ð �ˆ#ˆ#Õ&;Ñ&=Ô&=€CØ�7Š7�9ÑÔð ØˆuÝ�t�C˜Ñ#Ô#Ñ$Ô$×*Ò*Ñ,Ô,×2Ò2Ñ4Ô4Ð>¸€DØ�6Š>Ð<˜T W§]¢]¡_¤_×%:Ò%:Ñ%<Ô%<Ò<Ð<r   c                 óJ   — 	 ddl m}   | ¦   «         pdS # t          $ r Y dS w xY w)Nr   ©Úget_active_profile_nameÚdefault)Úhermes_cli.profilesr�   r`   rœ   s    r   Ú_active_profile_namer    N  sO   € ðØ?Ð?Ð?Ð?Ð?Ð?à&Ð&Ñ(Ô(Ð5¨IÐ5øÝð ð ð Øˆyˆyðøøøs   ‚ ”
"¡"úDict[str, str]c                 ó  — i } 	 ddl m} ddlm}m}  |¦   «         D ]Û}t          |dd¦  «        pt          |¦  «        }	 t           ||¦  «        ¦  «        dz  } ||¦  «        }|                     d¦  «        pi }t          |t          ¦  «        r|                     d¦  «        sŒ‹t          |                     d	¦  «        pd
|› �¦  «                             ¦   «         }	|	r|	| |<   ŒÌ# t          $ r Y ŒØw xY wn# t          $ r Y nw xY w| S )aÊ  Map ``profile name -> wake phrase`` for every wake-enabled profile.

    Reads each profile's own ``config.yaml`` raw (cheap, no full config merge).
    A profile is enrolled when its ``wake_word.enabled`` is truthy; its phrase
    defaults to ``"hey <profile>"`` when unset. Used by the sherpa engine to
    listen for every enrolled profile's phrase at once and route the wake to
    the matching profile. Best-effort: unreadable profiles are skipped.
    r   )Úread_user_config_raw)Úget_profile_dirÚlist_profilesÚnameNzconfig.yamlr^   r   r   zhey )r_   r£   rŸ   r¤   r¥   r‘   r(   r   rF   rE   rG   r.   r`   )
Úphrasesr£   r¤   r¥   r—   r¦   Úcfg_pathrm   Úwcr   s
             r   Úenrolled_profile_phrasesrª   W  sh  € ð !€GðØ:Ð:Ð:Ð:Ð:Ð:ØFÐFÐFÐFÐFÐFÐFÐFà!�M‘O”Oð 	ð 	ˆDÝ˜4 ¨Ñ.Ô.Ð;µ#°d±)´)ˆDðõ    °Ñ 5Ô 5Ñ6Ô6¸ÑF�Ø*Ð*¨8Ñ4Ô4�Ø—W’W˜[Ñ)Ô)Ð/¨R�Ý! "¥dÑ+Ô+ð °2·6²6¸)Ñ3DÔ3Dð ØÝ˜RŸVšV HÑ-Ô-Ð>°¸°°Ñ?Ô?×EÒEÑGÔG�Øð +Ø$*�G˜D‘MøøÝð ð ð Ø�ðøøøð	øõ ð ð ð Øˆðøøøà€NsB   „:C9 ¿A'C(Â&C9 Â'A C(Ã'C9 Ã(
C5Ã2C9 Ã4C5Ã5C9 Ã9
DÄDc                 ó   — dd l } dd l}|| fS ©Nr   )ÚnumpyÚsounddevice)Únpr’   s     r   rŽ   rŽ   }  s#   € ØÐÐÐØÐÐÐàˆrˆ6€Mr   c                 óT   — 	 t          ¦   «          dS # t          t          f$ r Y dS w xY w)NTF)rŽ   rQ   r�   r   r   r   Ú_audio_availabler±   „  s<   € ðÝ‰ŒˆØˆtøÝ�Ð!ð ð ð Øˆuˆuðøøøs   ‚ ’'¦'Úselectorc                óŒ  — d|i}	 |                       |d¦  «        }n+# t          $ r}t          |¦  «        |d<   |cY d}~S d}~ww xY wt          |t          ¦  «        �rf|                     d¦  «        }|rt          |¦  «        |d<   |                     d¦  «        }t          |t          t          f¦  «        rt          |¦  «        |d<   |                     d¦  «        }t          |t          t          f¦  «        rt          |¦  «        |d<   |                     d¦  «        }t          |t          t          f¦  «        r†t          |¦  «        |d	<   	 |                      t          |¦  «        ¦  «        }	t          |	t          ¦  «        r|	                     d¦  «        nd}
|
rt          |
¦  «        |d<   n# t          $ r Y nw xY w|S )
zÛResolve a PortAudio selector into JSON-safe diagnostics.

    Device discovery is diagnostic only. ``InputStream`` remains the authority
    on whether the selected device can actually open at the requested format.
    r²   r�   ÚerrorNr¦   rŒ   Údefault_samplerateÚhostapiÚhostapi_index)	r�   r`   r(   rE   rG   rF   rl   rp   Úquery_hostapis)r’   r²   Údetailsr—   Úer¦   r–   Úrater·   r¶   Úhostapi_names              r   Ú_describe_input_devicer½   Œ  s×  € ð  *¨8Ð4€GðØ×Ò ¨'Ñ2Ô2ˆˆøÝð ð ð Ý˜q™6œ6ˆ�ÑØˆˆˆˆˆˆøøøøðøøøõ �$�ÑÔñ Ø�xŠx˜ÑÔˆØð 	(Ý! $™iœiˆG�F‰OØ—8’8Ð0Ñ1Ô1ˆÝ�h¥¥e Ñ-Ô-ð 	:Ý,/°©M¬MˆGÐ(Ñ)Ø�xŠxÐ,Ñ-Ô-ˆÝ�d�S¥%˜LÑ)Ô)ð 	8Ý,1°$©K¬KˆGÐ(Ñ)ØŸš Ñ+Ô+ˆÝ�m¥c­5 \Ñ2Ô2ð 	Ý'*¨=Ñ'9Ô'9ˆG�OÑ$ðØ×+Ò+­C°Ñ,>Ô,>Ñ?Ô?�Ý6@ÀÍ$Ñ6OÔ6OÐY˜wŸ{š{¨6Ñ2Ô2Ð2ÐUY�Øð ;Ý),¨\Ñ):Ô):�G˜IÑ&øøÝð ð ð Ø�ðøøøð €Ns,   † �
A§A ºAÁ AÅA"F4 Æ4
GÇ Gr¹   c                óF  — t          |                      d¦  «        pd¦  «                             ¦   «         }|                      d¦  «        }|p|€dnt          |¦  «        }t          |                      d¦  «        pd¦  «                             ¦   «         }|r|› d|› d�n|S )Nr¦   r$   r²   úsystem defaultr¶   z (ú))r(   rF   r.   )r¹   r¦   r²   Úlabelr¶   s        r   Ú_device_labelrÂ   ±  s£   € Ýˆw�{Š{˜6Ñ"Ô"Ð( bÑ)Ô)×/Ò/Ñ1Ô1€DØ�{Š{˜:Ñ&Ô&€HØÐM¨Ð)9Ð%Ð%½sÀ8¹}¼}€EÝ�'—+’+˜iÑ(Ô(Ð.¨BÑ/Ô/×5Ò5Ñ7Ô7€GØ%,Ð7ˆeÐ!Ð!�wÐ!Ð!Ð!Ð!°%Ð7r   c                ó  — |                       d¦  «        }t          |t          t          f¦  «        rOt          |t          ¦  «        s:|dk    r4	 t          t          |¦  «        ¦  «        S # t          t          f$ r Y nw xY wt          S )zAUse the selected device's native rate when PortAudio reports one.rµ   r   )	rF   rE   rl   rp   r8   ÚroundÚOverflowErrorru   ÚSAMPLE_RATE)r¹   r»   s     r   Ú_capture_sample_raterÇ   ¹  sˆ   € à�;Š;Ð+Ñ,Ô,€DÝ�$��e˜Ñ%Ô%ð ­j¸½tÑ.DÔ.Dð ÈÐPQÊÈð	Ý•u˜T‘{”{Ñ#Ô#Ð#øÝ�zÐ*ð 	ð 	ð 	ØˆDð	øøøåÐs   ÁA* Á*A>Á=A>Úoutput_lengthc                ó(  — |                       || j        ¬¦  «                             d¦  «        }|j        |k    r/|                       || j        ¬¦  «                             d¦  «        S |j        dk    r|                      || j        ¬¦  «        S |j        |k    r`|                      d|j        |dz   | j        ¬¦  «        }| j         	                    ||dd…         ¦  «        |  
                    |¦  «        z  }nW|                      |j        | j        ¬¦  «        }|                      d|j        dz
  |¦  «        }|                      |||¦  «        }|                      |¦  «                             dd¦  «                             | j        ¦  «        S )z?Convert one native-rate capture block to an exact engine frame.©Údtypeéÿÿÿÿr   r{   Ni €ÿÿiÿ  )ÚasarrayÚfloat64ÚreshapeÚsizeÚint16ÚzerosÚlinspaceÚint64ÚaddÚreduceatÚdiffÚarangeÚinterpÚrintÚclipÚastype)r¯   ÚframerÈ   ÚsourceÚedgesÚvaluesÚsource_positionsÚtarget_positionss           r   Ú_resample_audio_framerã   Ä  s]  € à�ZŠZ˜ R¤ZˆZÑ0Ô0×8Ò8¸Ñ<Ô<€FØ„{�mÒ#Ð#Ø�zŠz˜% r¤xˆzÑ0Ô0×8Ò8¸Ñ<Ô<Ð<Ø„{�aÒÐØ�xŠx˜¨R¬XˆxÑ6Ô6Ð6à„{�]Ò"Ð"ð —’˜A˜vœ{¨M¸AÑ,=ÀRÄX�ÑNÔNˆØ”—’ ¨¨s°¨s¬Ñ4Ô4°r·w²w¸u±~´~ÑEˆˆð Ÿ9š9 V¤[¸¼
˜9ÑCÔCÐØŸ;š; q¨&¬+¸©/¸=ÑIÔIÐØ—’Ð+Ð-=¸vÑFÔFˆà�7Š7�6‰?Œ?×Ò ¨Ñ.Ô.×5Ò5°b´hÑ?Ô?Ð?r   c                ó”   — t           j        dk    r	 dS t           j        dk    rdt          | ¦  «        › d�S dt          | ¦  «        › d�S )zEPlatform-specific remediation for an armed stream delivering silence.r:   z�Microphone delivers only silence. Grant the Hermes backend microphone access in System Settings > Privacy & Security > Microphone, then toggle the wake word.Úwin32z&Microphone delivers only silence from z^. Set wake_word.input_device to a different PortAudio input device, then toggle the wake word.z=. Check the selected input device, then toggle the wake word.)r=   r<   rÂ   )r¹   s    r   Úsilent_audio_hintræ   Û  sw   € å
„|�xÒÐð5ð	
ð 	
õ
 „|�wÒÐð)µ]À7Ñ5KÔ5Kð )ð )ð )ð	
ð	Fµ¸wÑ1GÔ1Gð 	Fð 	Fð 	Fðr   c                  óH   — e Zd ZU dZdZded<   dZded<   dd
„Zdd„Zdd„Z	dS )Ú_Enginez?Minimal hotword-engine contract: feed int16 frames, get a bool.é   rl   Úframe_lengthNúOptional[tuple[str, str]]Ú
last_matchr)   r8   c                ó   — t           ‚rd   )ÚNotImplementedError©ÚselfrÝ   s     r   Úprocessz_Engine.processþ  s   € Ý!Ð!r   ÚNonec                ó   — dS )zDClear any internal audio/feature buffer (called on every (re)start).Nr   ©rð   s    r   Úresetz_Engine.reset  s   € àˆr   c                ó   — d S rd   r   rô   s    r   Úclosez_Engine.close  s   € Øˆr   ©r)   r8   ©r)   rò   )
r   r   r   r   rê   Ú__annotations__rì   rñ   rõ   r÷   r   r   r   rè   rè   ó  s|   € € € € € € ØIÐIà€LÐÐÐÑð -1€JÐ0Ð0Ð0Ñ0ð"ð "ð "ð "ðð ð ð ðð ð ð ð ð r   rè   rn   c                ó†   — t           j        | v p3|                      d¦  «        pt           j                             | ¦  «        S )N)z.onnxz.tflitez.ppn)r0   ÚsepÚendswithr1   Úexists)rn   s    r   Ú_looks_like_pathrÿ   	  s;   € å
Œ�%ˆð 	!Ø�>Š>Ð6Ñ7Ô7ð	!åŒ7�>Š>˜%Ñ Ô ðr   c                  ó6   — e Zd ZdZdZdd„Zdd„Zdd
„Zdd„ZdS )Ú_OpenWakeWordEngineu4   openWakeWord â€” free, local ONNX hotword detection.ré   rB   r!   c                óŒ  — ddl m} |                     dd¬¦  «         dd l}ddlm} t          |                     d¦  «        t          ¦  «        r|                     d¦  «        ni }t          |                     d¦  «        pt          ¦  «                             ¦   «         }t          |¦  «        }t          |¦  «        | _        t          |¦  «        | _        d| _        |d	k    r t%          ¦   «         s’	 |                     d
d¬¦  «         n2# t&          $ r%}t(                               d|¦  «         Y d }~nd }~ww xY wt%          ¦   «         s9t-          ¦   «         rt/          d¦  «        ‚t(                               d¦  «         d}|                     ¦   «         t4          v rt7          |¦  «        }	 |j                             |g¦  «         n2# t&          $ r%}t(                               d|¦  «         Y d }~nd }~ww xY w|g}	 ||	|¬¦  «        | _        t?          | j        j          !                    ¦   «         ¦  «        | _"        d S )Nr   ©Ú	lazy_depsúwake.openwakewordF©Úprompt)ÚModelr   Úmodelr+   úwake.openwakeword.tflitez,wake word: tflite runtime install failed: %sz{The wake word needs the tflite backend on this Mac, but its runtime is missing. Install it with: pip install ai-edge-litertu?   wake word: no tflite runtime available â€” falling back to onnxr&   z'openwakeword model download skipped: %s)Úwakeword_modelsrD   )#Útoolsr  Úensurer   Úopenwakeword.modelr  rE   rF   rG   r(   r5   r.   rL   ry   Ú
_thresholdr~   Ú_confirm_neededÚ_confirm_streakr[   r`   rI   rX   r?   ÚRuntimeErrorrJ   r/   Ú_BUNDLED_MODEL_ALIASESr7   ÚutilsÚdownload_modelsÚ_modelÚlistÚmodelsÚkeysÚ_labels)
rð   rB   r  r   r  rK   Ú	model_refr'   rº   r  s
             r   Ú__init__z_OpenWakeWordEngine.__init__  s  € Ø#Ð#Ð#Ð#Ð#Ð#à×ÒÐ,°UÐÑ;Ô;Ð;àÐÐÐØ,Ð,Ð,Ð,Ð,Ð,å)3°C·G²G¸NÑ4KÔ4KÍTÑ)RÔ)RÐZˆc�gŠg�nÑ%Ô%Ð%ÐXZˆÝ˜Ÿš Ñ(Ô(Ð?Õ,?Ñ@Ô@×FÒFÑHÔHˆ	Ý/°Ñ4Ô4ˆ	õ
 ' sÑ+Ô+ˆŒÝ3°CÑ8Ô8ˆÔØ ˆÔð ˜Ò Ð Õ)>Ñ)@Ô)@Ð ðPØ× Ò Ð!;ÀEÐ ÑJÔJÐJÐJøÝð Pð Pð PÝ—’ÐKÈQÑOÔOÐOÐOÐOÐOÐOÐOøøøøðPøøøå(Ñ*Ô*ð #Ý"Ñ$Ô$ð Ý&ðZñô ð õ —’Ð`ÑaÔaÐaØ"�	ð �?Š?ÑÔÕ 6Ð6Ð6Ý.¨yÑ9Ô9ˆIð	GØÔ×.Ò.°	¨{Ñ;Ô;Ð;Ð;øÝð 	Gð 	Gð 	GÝ�LŠLÐBÀAÑFÔFÐFÐFÐFÐFÐFÐFøøøøð	Gøøøà�ˆà�e¨FÈ	ÐRÑRÔRˆŒÝ˜DœKÔ.×3Ò3Ñ5Ô5Ñ6Ô6ˆŒˆˆs0   Ã5D Ä
D<ÄD7Ä7D<Æ1G Ç
G<ÇG7Ç7G<r)   r8   c                óþ   ‡ — ‰ j                              |¦  «        }t          ˆ fd„|                     ¦   «         D ¦   «         ¦  «        }|r+‰ xj        dz  c_        ‰ j        ‰ j        k    r	d‰ _        dS dS d‰ _        dS )Nc              3  ó.   •K  — | ]}|‰j         k    V — Œd S rd   )r  )Ú.0Úscorerð   s     €r   ú	<genexpr>z._OpenWakeWordEngine.process.<locals>.<genexpr>T  s*   øè è € ÐIÐI°�5˜DœOÒ+ÐIÐIÐIÐIÐIÐIr   r{   r   TF)r  ÚpredictÚanyrà   r  r  )rð   rÝ   ÚscoresÚovers   `   r   rñ   z_OpenWakeWordEngine.processR  s�   ø€ Ø”×$Ò$ UÑ+Ô+ˆÝÐIÐIÐIÐI¸¿º¹¼ÐIÑIÔIÑIÔIˆð ð 	ØÐ Ô  AÑ%Ð Ô ØÔ# tÔ';Ò;Ð;Ø'(�Ô$Ø�tØ�5Ø ˆÔØˆur   rò   c                ój   — d| _         	 | j                             ¦   «          d S # t          $ r Y d S w xY wr¬   )r  r  rõ   r`   rô   s    r   rõ   z_OpenWakeWordEngine.reset`  sM   € ð  !ˆÔð	ØŒK×ÒÑÔÐÐÐøÝð 	ð 	ð 	ØˆDˆDð	øøøs   ‰$ ¤
2±2c                ó.   — |                       ¦   «          d S rd   )rõ   rô   s    r   r÷   z_OpenWakeWordEngine.closei  ó   € Ø�
Š
‰Œˆˆˆr   N©rB   r!   rø   rù   ©	r   r   r   r   rê   r  rñ   rõ   r÷   r   r   r   r  r    sr   € € € € € Ø>Ð>ð €Lð97ð 97ð 97ð 97ðvð ð ð ðð ð ð ðð ð ð ð ð r   r  zhttps://github.com/k2-fsa/sherpa-onnx/releases/download/kws-models/sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01.tar.bz2z4sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01r   c                 ó.   — ddl m}   | ¦   «         dz  dz  S )Nr   ©Úget_hermes_homeÚcacher,   )Úhermes_constantsr-  r,  s    r   Ú_sherpa_model_rootr0  x  s,   € Ø0Ð0Ð0Ð0Ð0Ð0àˆ?ÑÔ˜wÑ&¨Ñ4Ð4r   ÚrootúOptional[Path]c                ó@  — | pt          ¦   «         } | t          z  }|dz                       ¦   «         r|S ddl}ddl}|                      dd¬¦  «         | t          › d�z  }t                               d¦  «         |j         	                    t          |¦  «         |                     |d¦  «        5 }|                     | d	¬
¦  «         ddd¦  «         n# 1 swxY w Y   |                     d¬¦  «         |dz                       ¦   «         st          d|› �¦  «        ‚|S )zBDownload + unpack the sherpa KWS model once; return its directory.ú
tokens.txtr   NT©ÚparentsÚexist_okz.tar.bz2z:wake word: downloading sherpa KWS model (one-time, ~13 MB)zr:bz2Údata)Úfilter)Ú
missing_okz sherpa KWS model unpack failed: )r0  Ú_SHERPA_KWS_MODEL_DIRrþ   ÚtarfileÚurllib.requestÚmkdirrI   r—   ÚrequestÚurlretrieveÚ_SHERPA_KWS_MODEL_URLÚopenÚ
extractallÚunlinkr  )r1  Útargetr<  ÚurllibÚarchiveÚtfs         r   Ú_ensure_sherpa_modelrI  ~  sm  € àÐ'Õ%Ñ'Ô'€DØÕ)Ñ)€FØ�Ñ×%Ò%Ñ'Ô'ð ØˆØ€N€N€NØÐÐÐà‡J‚J�t d€JÑ+Ô+Ð+ØÕ-Ð7Ð7Ð7Ñ7€GÝ
‡K‚KÐLÑMÔMÐMØ
„N×ÒÕ4°gÑ>Ô>Ð>Ø	�Š�g˜wÑ	'Ô	'ð +¨2Ø
�Š�d 6ˆÑ*Ô*Ð*ð+ð +ð +ñ +ô +ð +ð +ð +ð +ð +ð +øøøð +ð +ð +ð +à‡N‚N˜d€NÑ#Ô#Ð#Ø�\Ñ!×)Ò)Ñ+Ô+ð HÝÐF¸fÐFÐFÑGÔGÐGØ€Ms   Â0CÃCÃCc                  ó6   — e Zd ZdZdZdd„Zdd„Zdd
„Zdd„ZdS )Ú_SherpaKwsEngineuC  sherpa-onnx open-vocabulary keyword spotting â€” any typed phrase, zero training.

    The configured ``wake_word.phrase`` is BPE-tokenized at runtime against the
    model's vocabulary, so "hey hermes", "hey coder", or any other phrase works
    immediately. Here ``phrase`` is DETECTION config, not a cosmetic label.
    ré   rB   r!   c           	     ón  ‡— ddl m} |                     dd¬¦  «         dd l}ddlm} t          |                     d¦  «        t          ¦  «        r|                     d¦  «        ni }t          |                     d¦  «        pd	¦  «         	                    ¦   «         }|rt          |¦  «        nt          ¦   «         Š‰d
z                       ¦   «         st          d‰› �¦  «        ‚t          t          |d¦  «        pd¦  «         	                    ¦   «         }t          ¦   «         }||i}	t!          |                     dd¦  «        ¦  «        rNt#          ¦   «                              ¦   «         D ]-\  }
}|	                     | 	                    ¦   «         |
¦  «         Œ.t)          |	¦  «        } |d„ |D ¦   «         t          ‰d
z  ¦  «        dt          ‰dz  ¦  «        ¬¦  «        }dd l}i | _        |                     ddddd¬¦  «        }t1          ||¦  «        D ]l\  }}|                     ¦   «                              dd¦  «        }|	|         | j        |<   |                     d                     |¦  «        d|› d�z   ¦  «         Œm|                     ¦   «          |j        | _        d | _         ddtC          |¦  «        z  z   }d(ˆfd"„}| "                    t          ‰d
z  ¦  «         |d#¦  «         |d$¦  «         |d%¦  «        | j        |d&¬'¦  «        | _#        | j#         $                    ¦   «         | _%        d S ))Nr   r  úwake.sherpaFr  )Ú
text2tokenÚsherpaÚ	model_dirr$   r4  zsherpa KWS model not found at r   r   Úprofile_routingTc                ó6   — g | ]}|                      ¦   «         ‘ŒS r   )Úupper)r  Úps     r   ú
<listcomp>z-_SherpaKwsEngine.__init__.<locals>.<listcomp>»  s    € Ð(Ð(Ð(˜1ˆQ�WŠW‰YŒYÐ(Ð(Ð(r   Úbpez	bpe.model)ÚtokensÚtokens_typeÚ	bpe_modelÚwz.txtzhermes-kws-zutf-8)ÚmodeÚsuffixÚprefixÚdeleteÚencodingÚ r“   z @Ú
gš™™™™™©?gš™™™™™Ù?Úpatternr(   r)   c                ó    •— t          ‰                     | ¦  «        ¦  «        }|st          d‰› d| › �¦  «        ‚t          |d         ¦  «        S )Nzsherpa KWS model file missing: ú/r   )ÚsortedÚglobr  r(   )rb  ÚhitsÚds     €r   Ú_model_filez._SherpaKwsEngine.__init__.<locals>._model_file×  sR   ø€ Ý˜!Ÿ&š& ™/œ/Ñ*Ô*ˆDØð TÝ"Ð#RÀQÐ#RÐ#RÈÐ#RÐ#RÑSÔSÐSÝ�t˜A”w‘<”<Ðr   zencoder-*[!8].onnxzdecoder-*[!8].onnxzjoiner-*[!8].onnxr{   )rW  ÚencoderÚdecoderÚjoinerÚkeywords_fileÚkeywords_thresholdÚnum_threads)rb  r(   r)   r(   )&r  r  r  Úsherpa_onnxrN  rE   rF   rG   r(   r.   r   rI  rþ   r  rf   r    r8   rª   ÚitemsrW   r  ÚtempfileÚ_display_to_profileÚNamedTemporaryFileÚziprS  ÚreplaceÚwriter2   r÷   r¦   Ú_keywords_filerì   ry   ÚKeywordSpotterÚ_spotterÚcreate_streamÚ_stream)rð   rB   r  rp  rN  rK   rP  r   Úown_profileÚ
phrase_mapÚprofrT  r§   rW  rr  ÚkwÚtoksÚdisplayÚ	thresholdri  rh  s                       @r   r  z_SherpaKwsEngine.__init__Ÿ  sy  ø€ Ø#Ð#Ð#Ð#Ð#Ð#à×Ò˜¨uÐÑ5Ô5Ð5àÐÐÐØ*Ð*Ð*Ð*Ð*Ð*å#-¨c¯gªg°hÑ.?Ô.?ÅÑ#FÔ#FÐNˆc�gŠg�hÑÔÐÈBˆÝ˜Ÿš Ñ,Ô,Ð2°Ñ3Ô3×9Ò9Ñ;Ô;ˆ	Ø(ÐD�D�‰OŒOˆOÕ.BÑ.DÔ.DˆØ�LÑ ×(Ò(Ñ*Ô*ð 	EÝÐCÀÐCÐCÑDÔDÐDõ •T˜#˜xÑ(Ô(Ð8¨LÑ9Ô9×?Ò?ÑAÔAˆÝ*Ñ,Ô,ˆØ&,¨kÐ%:ˆ
Ý�—’Ð)¨4Ñ0Ô0Ñ1Ô1ð 	7Ý3Ñ5Ô5×;Ò;Ñ=Ô=ð 7ð 7‘��aØ×%Ò% a§g¢g¡i¤i°Ñ6Ô6Ð6Ð6å�zÑ"Ô"ˆà�Ø(Ð( Ð(Ñ(Ô(Ý�q˜<Ñ'Ñ(Ô(ØÝ˜!˜k™/Ñ*Ô*ð	
ñ 
ô 
ˆð 	ˆˆˆð 46ˆÔ Ø×(Ò(Ø˜V¨MÀ%ÐRYð )ñ 
ô 
ˆõ ˜7 FÑ+Ô+ð 	8ð 	8‰GˆAˆtØ—g’g‘i”i×'Ò'¨¨SÑ1Ô1ˆGØ0:¸1´ˆDÔ$ WÑ-Ø�HŠH�S—X’X˜d‘^”^Ð&6¨7Ð&6Ð&6Ð&6Ñ6Ñ7Ô7Ð7Ð7Ø
�Š‰
Œ
ˆ
Ø œgˆÔà59ˆŒð ˜3¥¨cÑ!2Ô!2Ñ2Ñ2ˆ	ð	 ð 	 ð 	 ð 	 ð 	 ð 	 ð $×2Ò2Ý�q˜<Ñ'Ñ(Ô(Ø�KÐ 4Ñ5Ô5Ø�KÐ 4Ñ5Ô5Ø�;Ð2Ñ3Ô3ØÔ-Ø(Øð 3ñ 
ô 
ˆŒð ”}×2Ò2Ñ4Ô4ˆŒˆˆr   r)   r8   c                ó|  — dd l }|                     ||j        ¬¦  «        dz  }| j                             t
          |¦  «         d}| j                             | j        ¦  «        r×| j                             | j        ¦  «         | j         	                    | j        ¦  «        }|rxd}t          |¦  «        }|                     dd¦  «                             ¦   «         | j                             |d¦  «        f| _        | j                             | j        ¦  «         | j                             | j        ¦  «        °×|S )	Nr   rÊ   g      à@FTr“   r`  r$   )r­   rÍ   Úfloat32r|  Úaccept_waveformrÆ   rz  Úis_readyÚdecode_streamÚ
get_resultr(   rv  r/   rs  rF   rì   Úreset_stream)rð   rÝ   r¯   ÚsamplesÚfiredÚresultr‚  s          r   rñ   z_SherpaKwsEngine.processè  s!  € ØÐÐÐà—*’*˜U¨"¬*�*Ñ5Ô5¸Ñ?ˆØŒ×$Ò$¥[°'Ñ:Ô:Ð:ØˆØŒm×$Ò$ T¤\Ñ2Ô2ð 	9ØŒM×'Ò'¨¬Ñ5Ô5Ð5Ø”]×-Ò-¨d¬lÑ;Ô;ˆFØð 9Ø�Ý˜f™+œ+�à—O’O C¨Ñ-Ô-×3Ò3Ñ5Ô5ØÔ,×0Ò0°¸"Ñ=Ô=ð#�”ð
 ”×*Ò*¨4¬<Ñ8Ô8Ð8ð Œm×$Ò$ T¤\Ñ2Ô2ð 	9ð ˆr   rò   c                óf   — 	 | j                              ¦   «         | _        d S # t          $ r Y d S w xY wrd   )rz  r{  r|  r`   rô   s    r   rõ   z_SherpaKwsEngine.resetü  sB   € ð	Øœ=×6Ò6Ñ8Ô8ˆDŒLˆLˆLøÝð 	ð 	ð 	ØˆDˆDð	øøøs   ‚" ¢
0¯0c                ó\   — 	 t          j        | j        ¦  «         d S # t          $ r Y d S w xY wrd   )r0   rD  rx  r�   rô   s    r   r÷   z_SherpaKwsEngine.close  sB   € ð	ÝŒI�dÔ)Ñ*Ô*Ð*Ð*Ð*øÝð 	ð 	ð 	ØˆDˆDð	øøøó   ‚ �
+ª+Nr)  rø   rù   r*  r   r   r   rK  rK  “  sz   € € € € € ðð ð €LðG5ð G5ð G5ð G5ðRð ð ð ð(ð ð ð ðð ð ð ð ð r   rK  c                  ó*   — e Zd ZdZdd„Zdd„Zdd	„Zd
S )Ú_PorcupineEngineu@   Picovoice Porcupine â€” premium, on-device, needs an access key.rB   r!   c                óR  — ddl m} |                     dd¬¦  «         dd l}t	          j        d¦  «        pd                     ¦   «         }|st          d¦  «        ‚t          | 	                    d	¦  «        t          ¦  «        r| 	                    d	¦  «        ni }t          | 	                    d
¦  «        pd¦  «                             ¦   «         }dt          |¦  «        z
  }||gdœ}t          |¦  «        r|g|d<   n|g|d<    |j        di |¤Ž| _        | j        j        | _        d S )Nr   r  úwake.porcupineFr  ÚPORCUPINE_ACCESS_KEYr$   zcPorcupine wake word requires PORCUPINE_ACCESS_KEY (get a free key at https://console.picovoice.ai).Ú	porcupineÚkeywordÚjarvisrs   )Ú
access_keyÚsensitivitiesÚkeyword_pathsÚkeywordsr   )r  r  r  Úpvporcupiner0   Úgetenvr.   r  rE   rF   rG   r(   ry   rÿ   ÚcreateÚ
_porcupinerê   )	rð   rB   r  r�  r™  rK   r—  Úporcupine_sensitivityÚkwargss	            r   r  z_PorcupineEngine.__init__  sW  € Ø#Ð#Ð#Ð#Ð#Ð#à×ÒÐ)°%ÐÑ8Ô8Ð8àÐÐÐå”iÐ 6Ñ7Ô7Ð=¸2×DÒDÑFÔFˆ
Øð 	ÝðDñô ð õ
 '1°·²¸Ñ1EÔ1EÅtÑ&LÔ&LÐTˆc�gŠg�kÑ"Ô"Ð"ÐRTˆÝ�c—g’g˜iÑ(Ô(Ð4¨HÑ5Ô5×;Ò;Ñ=Ô=ˆð
 !$¥l°3Ñ&7Ô&7Ñ 7Ðà0:ÐNcÐMdÐ!eÐ!eˆÝ˜GÑ$Ô$ð 	+Ø'. iˆF�?Ñ#Ð#à") ˆF�:Ñà,˜+Ô,Ð6Ð6¨vÐ6Ð6ˆŒØ œOÔ8ˆÔÐÐr   r)   r8   c                ó>   — | j                              |¦  «        dk    S r¬   )r   rñ   rï   s     r   rñ   z_PorcupineEngine.process-  s   € àŒ×&Ò& uÑ-Ô-°Ò2Ð2r   rò   c                ó\   — 	 | j                              ¦   «          d S # t          $ r Y d S w xY wrd   )r   r^  r`   rô   s    r   r÷   z_PorcupineEngine.close1  sC   € ð	ØŒO×"Ò"Ñ$Ô$Ð$Ð$Ð$øÝð 	ð 	ð 	ØˆDˆDð	øøør�  Nr)  rø   rù   )r   r   r   r   r  rñ   r÷   r   r   r   r’  r’    sV   € € € € € ØJÐJð9ð 9ð 9ð 9ð>3ð 3ð 3ð 3ðð ð ð ð ð r   r’  c                óº   — t          | ¦  «        }|dk    rt          | ¦  «        S |dv rt          | ¦  «        S |dv rt          | ¦  «        S t	          d|›�¦  «        ‚)Nr–  ©rO  úsherpa-onnxÚkwsrB  )r   Úowwr…   zUnknown wake_word provider: )ri   r’  rK  r  ru   )rB   r   s     r   Ú_build_enginerª  8  sq   € Ý˜‰~Œ~€HØ�;ÒÐÝ Ñ$Ô$Ð$ØÐ;Ð;Ð;Ý Ñ$Ô$Ð$ØÐ3Ð3Ð3Ý" 3Ñ'Ô'Ð'Ý
Ð@°HÐ@Ð@Ñ
AÔ
AÐAr   c                 ó‚   — 	 ddl m} m}m}  |¦   «         } ||¦  «        o | |¦  «        dk    S # t          $ r Y dS w xY w)u  Is a speech-to-text provider configured and enabled?

    A wake without STT arms the mic but every captured utterance dies at
    transcription â€” a useless (and confusing) experience. Same standard as
    voice mode's ``check_voice_requirements``: enabled + a real provider.
    r   )Ú_get_providerÚ_load_stt_configÚis_stt_enabledÚnoneF)Útools.transcription_toolsr¬  r­  r®  r`   )r¬  r­  r®  Ú
stt_configs       r   Ú
_stt_readyr²  G  sy   € ðØ]Ð]Ð]Ð]Ð]Ð]Ð]Ð]Ð]Ð]à%Ð%Ñ'Ô'ˆ
Øˆ~˜jÑ)Ô)ÐQ¨m¨m¸JÑ.GÔ.GÈ6Ò.QÐQøÝð ð ð Øˆuˆuðøøøs   ‚-0 °
>½>c                 óz  — 	 ddl m} m}  |  |¦   «         ¦  «        }n# t          $ r Y dS w xY wddddœ}|                     |¦  «        }|�B	 dd	lm} |                     |¦  «        s|                     ¦   «         S n# t          $ r Y dS w xY w	 dd
l m	} t           |¦   «         ¦  «        S # t          $ r Y dS w xY w)uŸ  Can the configured text-to-speech provider run (or install at first use)?

    The wake flow is fully hands-free (wake â†’ speak â†’ hear the reply); without
    TTS the reply is silent and the loop is pointless.

    PROBE, not an installer: ``check_tts_requirements`` lazily pip-installs the
    provider SDK via ``_import_*`` â†’ ``lazy_deps.ensure`` â€” running that inside
    a status poll froze wake.status for the length of a pip install (and a
    failed install marked the wake word unavailable, unmounting the desktop
    ear). When the provider's deps aren't installed yet, "installable at first
    use" counts as ready and we never touch pip from here.
    r   )r¬  Ú_load_tts_configFztts.edgeztts.elevenlabsztts.mistral)ÚedgeÚ
elevenlabsÚmistralNr  )Úcheck_tts_requirements)Útools.tts_toolr¬  r´  r`   rF   r  r  Úis_availableÚ_allow_lazy_installsr¸  r8   )r¬  r´  r   Ú_LAZY_TTS_FEATURESÚfeaturer  r¸  s          r   Ú
_tts_readyr¾  W  sH  € ðØBÐBÐBÐBÐBÐBÐBÐBà �=Ð!1Ð!1Ñ!3Ô!3Ñ4Ô4ˆˆøÝð ð ð Øˆuˆuðøøøð Ø&Ø ðð Ðð
 !×$Ò$ XÑ.Ô.€GØÐð	Ø'Ð'Ð'Ð'Ð'Ð'à×)Ò)¨'Ñ2Ô2ð 8à ×5Ò5Ñ7Ô7Ð7ð8øõ ð 	ð 	ð 	Ø�5�5ð	øøøðØ9Ð9Ð9Ð9Ð9Ð9åÐ*Ð*Ñ,Ô,Ñ-Ô-Ð-øÝð ð ð Øˆuˆuðøøøs0   ‚ ž
,«,Á.A= Á=
BÂ
BÂB, Â,
B:Â9B:c                óà  — | �| nt          ¦   «         } t          | ¦  «        }ddlm} |dk    rd}n	|dv rd}nd}|                     |¦  «        }|                     ¦   «         }|rt          ¦   «         nd	}d
}t          ¦   «         }t          ¦   «         }	d}
d
}|dvr:t          | ¦  «        }|dk    r%t          ¦   «         p|                     d¦  «        p|}|dk    r-t          j        d¦  «        pd                     ¦   «         sd	}d}
nj|s|s|                     |¦  «        pd}
nN|sd}
nI|r|st          | ¦  «        dk    rd}
n/|r|	s+d                     d„ d|fd|	ffD ¦   «         ¦  «        }d|› d�}
t          | ¦  «        }|rt#          ¦   «         nd	}|dk    r#|p| o|}|r|
s|
                     d¦  «        rd}
n|r|p| o|}|r|s|
sd}
|o|o|	o|o|||||||||	t'          | ¦  «        |
dœS )zDReport whether wake-word detection can run, with a remediation hint.Nr   r  r–  r”  r¦  rM  r  FTr$   )r–  rO  r§  r¨  rB  r+   r
  r•  zDSet PORCUPINE_ACCESS_KEY (free key at https://console.picovoice.ai).zNThe wake word needs the tflite runtime on this Mac: pip install ai-edge-litertr…   zHMicrophone capture needs sounddevice + numpy and a working audio device.z and c              3  ó$   K  — | ]\  }}|°|V — Œd S rd   r   )r  r¦   Úoks      r   r!  z/check_wake_word_requirements.<locals>.<genexpr>³  s@   è è € ð 
ð 
Ù�T˜2Ðacð
Øð
ð 
ð 
ð 
ð 
ð 
r   zspeech-to-textztext-to-speechzWake word needs uN    configured â€” run `hermes tools` (Voice section) or see the voice-mode docs.r†   zMicrophone capture needsu¨   No local microphone on this backend. Remote desktop can stream the client mic â€” set wake_word.capture: client or use a desktop build with client-capture wake support.)Ú	availabler   Údeps_availableÚaudio_availableÚlocal_input_availabler   Úaccess_key_setÚstt_availableÚtts_availabler   Úhint)ra   ri   r  r  rº  r»  r±   r²  r¾  rL   r[   r0   rž  r.   Úfeature_install_commandrŠ   r2   r‰   Ú
startswithr�   )rB   r   r  r½  Údeps_okÚlazy_okÚaudio_okÚkey_okÚstt_okÚtts_okrÉ  Ú	tflite_okr'   ÚmissingÚcapture_modeÚlocal_input_okÚmic_oks                    r   Úcheck_wake_word_requirementsr×  ƒ  sý  € à�ˆ#ˆ#Õ&;Ñ&=Ô&=€CÝ˜‰~Œ~€HØÐÐÐÐÐà�;ÒÐØ"ˆˆØ	Ð=Ð	=Ð	=Øˆˆà%ˆØ×$Ò$ WÑ-Ô-€GØ×,Ò,Ñ.Ô.€Gð &-Ð7ÕÑ!Ô!Ð!°%€HØ€Fõ ‰\Œ\€FÝ‰\Œ\€FØ€Dð €IØÐLÐLÐLÝ/°Ñ4Ô4ˆ	Ø˜Ò Ð Ý-Ñ/Ô/Ðp°9×3IÒ3IÐJdÑ3eÔ3eÐpÐipˆIà�;ÒÐ­¬	Ð2HÑ(IÔ(IÐ(OÈR×'VÒ'VÑ'XÔ'XÐØˆØUˆˆØð @˜Wð @Ø×0Ò0°Ñ9Ô9Ð?¸RˆˆØð 	@Ø_ˆˆØ	ð @˜ð @Õ&:¸3Ñ&?Ô&?À7Ò&JÐ&JØYˆˆØð @˜6ð @Ø—,’,ð 
ð 
Ø#3°VÐ"<Ð?OÐQWÐ>XÐ!Yð
ñ 
ô 
ñ 
ô 
ˆð? 7ð ?ð ?ð ?ˆõ (¨Ñ,Ô,€LØ4;ÐFÕ.Ñ0Ô0Ð0À€Nà�xÒÐØÐ5 ˜[Ð4¨WˆØð 	˜4ð 	à�ŠÐ9Ñ:Ô:ð Ø�øàÐ&˜hÐD°¨KÐ,C¸GˆØð 	˜8ð 	¨Dð 	ð:ð ð ÐJ ÐJ¨6ÐJ°iÐJÀFØØ!Ø#Ø!/ØØ ØØÝ˜cÑ"Ô"Øðð ð r   c                  ó|   — e Zd ZdZedddfd d„Zed!d„¦   «         Zd"d„Zd"d„Z	d"d„Z
d"d„Zd"d„Zd"d„Zd"d„Zd#d„ZdS )$ÚWakeWordDetectorzñBackground hotword listener. Fires ``on_wake()`` when the phrase is heard.

    The engine is built once and kept alive across pause/resume; only the audio
    stream + reader thread cycle, so toggling the mic for a voice turn is cheap.
    NFÚenginerè   Úon_wakeúCallable[[], None]Úcooldownrp   Ú
on_failureú.Optional[Callable[['WakeWordDetector'], None]]r   rj   Úexternal_audior8   c                ó¦  — || _         || _        || _        || _        || _        t          |¦  «        | _        | j        rddddœnd|i| _        d | _        t          j
        ¦   «         | _        t          j
        ¦   «         | _        d| _        t          j        ¦   «         | _        dd l}|                     d¬	¦  «        | _        d
| _        d| _        d S )Nr†   zclient capturer‡   )r²   r¦   r¶   r²   rr   r   é@   )ÚmaxsizeF)rÚ  rÛ  rÝ  rÞ  r   r8   rà  Úinput_device_detailsÚ_threadÚ	threadingÚEventÚ_stopÚ_callback_inflightÚ
_last_fireÚLockÚ_lockÚqueueÚQueueÚ_audio_qÚaudio_silentÚ_silent_frames)rð   rÚ  rÛ  rÝ  rÞ  r   rà  Ú_queues           r   r  zWakeWordDetector.__init__å  sÜ   € ð
 ˆŒØˆŒØ ˆŒØ$ˆŒØ(ˆÔÝ" >Ñ2Ô2ˆÔð Ô"ð,˜Ð+;ÈÐQÐQÐQà˜lÐ+ð 	Ô!ð
 48ˆŒÝ”_Ñ&Ô&ˆŒ
Ý"+¤/Ñ"3Ô"3ˆÔØˆŒÝ”^Ñ%Ô%ˆŒ
àÐÐÐà-3¯\ª\À"¨\Ñ-EÔ-EˆŒð "ˆÔØˆÔÐÐr   r)   c                ó@   — | j         }|d uo|                     ¦   «         S rd   )rå  Úis_alive©rð   Úts     r   ÚrunningzWakeWordDetector.running  s    € àŒLˆØ˜ˆ}Ð- §¢¡¤Ð-r   rò   c                ód  — | j         sdS 	 ddl}n# t          $ r Y dS w xY wt          |t          t
          t          f¦  «        r|                     ||j        ¬¦  «        }n/| 	                    ||j        ¬¦  «         
                    d¦  «        }t          | j        j        ¦  «        }|dk    rdS d}t          |j        d         ¦  «        }||k     rÞ||||z   …         }||z  }|j        d         |k     r0|                     ||j        ¬¦  «        }||d|j        d         …<   |}	 | j                             |¦  «         ng# t          $ rZ 	 | j                             ¦   «          n# t          $ r Y nw xY w	 | j                             |¦  «         n# t          $ r Y nw xY wY nw xY w||k     °ÜdS dS )zäEnqueue one int16 mono frame (or raw bytes) for client capture.

        Frame length should match ``engine.frame_length`` (typically 1280 samples
        at 16 kHz). Short frames are zero-padded; long frames are split.
        Nr   rÊ   rÌ   )rà  r­   r`   rE   ÚbytesÚ	bytearrayÚ
memoryviewÚ
frombufferrÑ   rÍ   rÏ   rl   rÚ  rê   ÚshaperÒ   rï  Ú
put_nowaitÚ
get_nowait)	rð   Ú	pcm_int16r¯   ÚarrÚflÚoffsetr}   ÚchunkÚpads	            r   ÚfeedzWakeWordDetector.feed	  s
  € ð Ô"ð 	ØˆFð	ØÐÐÐÐøÝð 	ð 	ð 	ØˆFˆFð	øøøå�i¥%­µJÐ!?Ñ@Ô@ð 	DØ—-’- 	°´�-Ñ:Ô:ˆCˆCà—*’*˜Y¨b¬h�*Ñ7Ô7×?Ò?ÀÑCÔCˆCÝ�”Ô)Ñ*Ô*ˆØ�Š7ˆ7ØˆFàˆÝ�”	˜!”ÑÔˆØ�qŠjˆjØ˜ ¨"¡Ð,Ô-ˆEØ�b‰LˆFØŒ{˜1Œ~ Ò"Ð"Ø—h’h˜r¨¬�hÑ2Ô2�Ø(-�Ð$�e”k !”nÐ$Ñ%Ø�ðØ”×(Ò(¨Ñ/Ô/Ð/Ð/øÝð 	ð 	ð 	ðØ”M×,Ò,Ñ.Ô.Ð.Ð.øÝ ð ð ð Ø�DðøøøðØ”M×,Ò,¨UÑ3Ô3Ð3Ð3øÝ ð ð ð Ø�Dðøøøøøð	øøøð �qŠjˆjˆjˆjˆjˆjsf   ‹ �
�Ä&E Å
F%ÅE&Å%F%Å&
E3Å0F%Å2E3Å3F%Å7FÆF%Æ
FÆF%ÆFÆF%Æ$F%c                óH  — | j         5  | j        �'| j                             ¦   «         r	 ddd¦  «         dS | j                             ¦   «          t          j        ¦   «         }g }t          j        | j        ||fdd¬¦  «        | _        | j         	                    ¦   «          ddd¦  «         n# 1 swxY w Y   | 
                    t          ¦  «        s#|                      ¦   «          t          d¦  «        ‚|r*|                      ¦   «          t          d¦  «        |d         ‚dS )z@Open the mic (or client feeder) and begin listening. Idempotent.NTz	wake-word)rE  ÚargsÚdaemonr¦   z1Timed out while opening the wake-word microphone.z(Failed to open the wake-word microphone.r   )rì  rå  rô  rè  Úclearræ  rç  ÚThreadÚ_runÚstartÚwaitÚ_START_TIMEOUT_SECONDSÚ_halt_threadÚTimeoutErrorr  )rð   ÚreadyÚstartup_errorss      r   r  zWakeWordDetector.start3  s‡  € àŒZð 	!ð 	!ØŒ|Ð'¨D¬L×,AÒ,AÑ,CÔ,CÐ'Øð	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð ŒJ×ÒÑÔÐÝ”OÑ%Ô%ˆEØ24ˆNÝ$Ô+Ø”yØ˜^Ð,ØØ ð	ñ ô ˆDŒLð ŒL×ÒÑ Ô Ð ð	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!øøøð 	!ð 	!ð 	!ð 	!ð �zŠzÕ0Ñ1Ô1ð 	TØ×ÒÑÔÐÝÐRÑSÔSÐSØð 	bØ×ÒÑÔÐÝÐIÑJÔJÐP^Ð_`ÔPaÐað	bð 	bs   ˆ"B.·A+B.Â.B2Â5B2c                ó.   — |                       ¦   «          d S rd   )r  rô   s    r   ÚpausezWakeWordDetector.pauseJ  s   € Ø×ÒÑÔÐÐÐr   c                ó.   — |                       ¦   «          d S rd   )r  rô   s    r   ÚresumezWakeWordDetector.resumeM  r(  r   c                ó`   — |                       ¦   «          | j                             ¦   «          d S rd   )r  rÚ  r÷   rô   s    r   ÚstopzWakeWordDetector.stopP  s.   € Ø×ÒÑÔÐØŒ×ÒÑÔÐÐÐr   c                ó   — | j         5  | j                             ¦   «          | j        }|�+|t	          j        ¦   «         ur|                     d¬¦  «         | j        |u rd | _        d d d ¦  «         d S # 1 swxY w Y   d S )Nr	   ©Útimeout)rì  rè  Úsetrå  ræ  Úcurrent_threadr2   rõ  s     r   r  zWakeWordDetector._halt_threadT  sÇ   € ØŒZð 	$ð 	$ØŒJ�NŠNÑÔÐØ”ˆAØˆ} ­)Ô*BÑ*DÔ*DÐ!DÐ!DØ—’˜s�Ñ#Ô#Ð#ØŒ|˜qÐ Ð Ø#�”ð	$ð 	$ð 	$ñ 	$ô 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øøøð 	$ð 	$ð 	$ð 	$ð 	$ð 	$s   ˆAA3Á3A7Á:A7c                ó  — 	 |                       ¦   «          n2# t          $ r%}t                               d|¦  «         Y d }~nd }~ww xY w| j                             ¦   «          d S # | j                             ¦   «          w xY w)Nzwake word callback failed: %s)rÛ  r`   rI   rJ   ré  r
  )rð   rº   s     r   Ú_dispatch_wakezWakeWordDetector._dispatch_wake]  s‘   € ð	,Ø�LŠL‰NŒNˆNˆNøÝð 	?ð 	?ð 	?Ý�NŠNÐ:¸AÑ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>øøøøð	?øøøð Ô#×)Ò)Ñ+Ô+Ð+Ð+Ð+øˆDÔ#×)Ò)Ñ+Ô+Ð+Ð+øøøs*   ‚ –A$ —
A¡A¼A$ ÁAÁA$ Á$A?r  úthreading.Eventr  úlist[BaseException]c           
     óR  — | j         j        }|}t          }d }d }| j        rO	 	 | j                             ¦   «          Œ# t          $ r Y nw xY wt                               d|t          ¦  «         �nâ	 t          ¦   «         \  }}nc# t          t          f$ rO}	t                               d|	¦  «         |                     |	¦  «         |                     ¦   «          Y d }	~	d S d }	~	ww xY wt          || j        ¦  «        | _        t%          | j        ¦  «        }t'          dt)          t+          ||z  t          z  ¦  «        ¦  «        ¦  «        }t                               d| j                             d¦  «        pd| j        | j                             d¦  «        pd	| j                             d
¦  «        pd	|t          ¦  «         	 |                     | j        |dd|¬¦  «        }|                     ¦   «          n\# t          $ rO}	t                               d|	¦  «         |                     |	¦  «         |                     ¦   «          Y d }	~	d S d }	~	ww xY w	 | j                              ¦   «          n# t          $ r Y nw xY wt                               d|t          | j        ¦  «         |                     ¦   «          d}
t'          dt)          t4          t          z  t'          d|¦  «        z  ¦  «        ¦  «        }	 | j                             ¦   «         �s$	 | j        rR	 | j                             d¬¦  «        }n2# t          $ r% | xj        dz  c_        | j        |k    rd| _        Y Œmw xY w|}n|                     |¦  «        \  }}nM# t          $ r@}	t                                d|	¦  «         | j                             ¦   «          }
Y d }	~	�nld }	~	ww xY wtC          |dd¦  «        dk    r|d d …df         n|}|t          k    rtE          |||¦  «        }	 tG          |¦  «        r.t)          tI          |¦  «                             ¦   «         ¦  «        nd}n# t          $ r tJ          dz   }Y nw xY w|tJ          k    r\| xj        dz  c_        | j        |k    r@d| _        t                                dtJ          t4          tM          | j        ¦  «        ¦  «         n6| j        r/| j        rt                               d¦  «         d| _        d| _        	 | j          '                    |¦  «        }n3# t          $ r&}	t           (                    d|	¦  «         Y d }	~	�ŒXd }	~	ww xY w|rÂtS          j*        ¦   «         }|| j+        z
  | j,        k    r‚|| _+        t                               d¦  «         | j-                             ¦   «         sG| j-                             ¦   «          t]          j/        | j0        dd¬¦  «                             ¦   «          nt           (                    d¦  «         | j                             ¦   «         �¯$|�:	 | 1                    ¦   «          | 2                    ¦   «          n# t          $ r Y nw xY wt                               d¦  «         |
r| j3        �|  3                    | ¦  «         d S d S d S # |�:	 | 1                    ¦   «          | 2                    ¦   «          n# t          $ r Y nw xY wt                               d¦  «         |
r| j3        �|  3                    | ¦  «         w w w xY w)NTuL   wake word: client-capture mode (frame=%d, rate=%d) â€” waiting for wake.feedz*wake word: audio libraries unavailable: %sr{   zmwake word: opening microphone device=%s selector=%r hostapi=%s default_rate=%s capture_rate=%d engine_rate=%dr¦   r¿   r¶   Úunknownrµ   rÑ   )ÚdeviceÚ
samplerater–   rË   Ú	blocksizez(wake word: failed to open microphone: %sz5wake word: listening (frame=%d, rate=%d, external=%s)Fg      Ð?r  z wake word: stream read error: %sÚndimé   r   z;wake word: mic delivers only silence (peak<=%d for %ds); %su0   wake word: mic audio detected â€” stream healthyzwake word: engine error: %su.   wake word: phrase detected â€” firing callbackzwake-word-callback)rE  r	  r¦   u0   wake word: detection within cooldown â€” ignoredzwake word: stream closed)4rÚ  rê   rÆ   rà  rï  rÿ  r`   rI   r—   rŽ   rQ   r�   r´   Úappendr  r½   r   rä  rÇ   rw   rl   rÄ   rF   ÚInputStreamr  rõ   Ú_SILENCE_ALERT_SECONDSrè  Úis_setrñ  rð  ÚreadrJ   r‘   rã   ÚlenÚabsÚ_SILENCE_PEAKræ   rñ   rX   ÚtimeÚ	monotonicrê  rÝ  ré  ræ  r  r   r  r÷   rÞ  )rð   r  r  rê   Úcapture_frame_lengthÚcapture_rater¯   Ústreamr’   rº   ÚfailedÚsilent_alert_framesrÝ   r8  Ú	_overflowÚpeakrŒ  Únows                     r   r  zWakeWordDetector._rune  sÀ  € à”{Ô/ˆØ+ÐÝ"ˆØˆØˆàÔð 0	ðð/Ø”M×,Ò,Ñ.Ô.Ð.ð/øåð ð ð Ø�ðøøøå�KŠKØ^Ø�kñô ð ñ ð
Ý&™œ‘��B�BøÝ¥Ð)ð ð ð Ý—’ÐIÈ1ÑMÔMÐMØ×%Ò% aÑ(Ô(Ð(Ø—	’	‘”�Ø�����øøøøð	øøøõ )?¸rÀ4ÔCTÑ(UÔ(UˆDÔ%Ý/°Ô0IÑJÔJˆLÝ#&Ø•3•u˜\¨LÑ8½;ÑFÑGÔGÑHÔHñ$ô $Ð õ �KŠKðAàÔ)×-Ò-¨fÑ5Ô5ÐIÐ9IØÔ!ØÔ)×-Ò-¨iÑ8Ô8ÐE¸IØÔ)×-Ò-Ð.BÑCÔCÐPÀyØÝñ	ô 	ð 	ðØŸšØÔ,Ø+ØØ!Ø2ð (ñ ô �ð —’‘”��øÝð ð ð Ý—’ÐGÈÑKÔKÐKØ×%Ò% aÑ(Ô(Ð(Ø—	’	‘”�Ø�����øøøøð	øøøð	ØŒK×ÒÑÔÐÐøÝð 	ð 	ð 	ØˆDð	øøøõ 	�ŠÐKØ ¥+¨tÔ/Bñ	Dô 	Dð 	Dà�	Š	‰ŒˆØˆõ " !¥SÕ)?Å+Ñ)MÕPSÐTUÐWcÑPdÔPdÑ)dÑ%eÔ%eÑfÔfÐðD	&Ø”j×'Ò'Ñ)Ô)ñ 9YðØÔ*ð Lð%Ø$(¤M×$5Ò$5¸dÐ$5Ñ$CÔ$C˜E˜EøÝ(ð %ð %ð %à Ð/Ô/°1Ñ4Ð/Ô/Ø#Ô2Ð6IÒIÐIØ48 Ô 1Ø$˜Hð%øøøð  %˜˜à*0¯+ª+Ð6JÑ*KÔ*K™˜˜iøøÝ ð ð ð Ý—N’NÐ#EÀqÑIÔIÐIØ!%¤×!2Ò!2Ñ!4Ô!4Ð4�FØ�E�E�E‘Eøøøøðøøøõ '.¨d°F¸AÑ&>Ô&>À!Ò&CÐ&C˜˜Q˜Q˜Q ˜Tœ
˜
È�Ø¥;Ò.Ð.Ý1°"°e¸\ÑJÔJ�Eð-Ý47¸±J´JÐE�3�s 5™zœzŸ~š~Ñ/Ô/Ñ0Ô0Ð0ÀA�D�DøÝ ð -ð -ð -Ý(¨1Ñ,�D�D�Dð-øøøà�=Ò(Ð(ØÐ'Ô'¨1Ñ,Ð'Ô'ØÔ*Ð.AÒAÐAØ,0˜Ô)ÝŸšØYÝ)Õ+AÝ-¨dÔ.GÑHÔHñô ð øð
 Ô(ð .ØÔ(ð XÝŸšÐ$VÑWÔWÐWØ*+�DÔ'Ø(-�DÔ%ðØ œK×/Ò/°Ñ6Ô6�E�EøÝ ð ð ð Ý—L’LÐ!>ÀÑBÔBÐBØ�H�H�H‘Høøøøðøøøð ð YÝœ.Ñ*Ô*�CØ˜Tœ_Ñ,°´Ò=Ð=Ø*-˜œÝŸšÐ$TÑUÔUÐUØ#Ô6×=Ò=Ñ?Ô?ð &Ø Ô3×7Ò7Ñ9Ô9Ð9Ý%Ô,Ø'+Ô':Ø'+Ø%9ðñ ô ÷ $še™gœg˜gøåŸšÐ%WÑXÔXÐXðs ”j×'Ò'Ñ)Ô)ñ 9Yðv Ð!ðØ—K’K‘M”M�MØ—L’L‘N”N�N�NøÝ ð ð ð Ø�Dðøøøå�KŠKÐ2Ñ3Ô3Ð3Øð &˜$œ/Ð5Ø—’ Ñ%Ô%Ð%Ð%Ð%ð&ð &Ð5Ð5øð Ð!ðØ—K’K‘M”M�MØ—L’L‘N”N�N�NøÝ ð ð ð Ø�Dðøøøå�KŠKÐ2Ñ3Ô3Ð3Øð &˜$œ/Ð5Ø—’ Ñ%Ô%Ð%Ð%ð&Ð5øøøs:  ¢= ½
A
Á	A
Á1B ÂC#ÂACÃC#Ç3G6 Ç6
IÈ AI
É
IÉI- É-
I:É9I:Ë6Z. ÌN ÌL5 Ì4N Ì5,M$Í!N Í"Z. Í#M$Í$N ÎZ. Î
OÎ5OÏZ. ÏOÏAZ. Ð?Q ÑZ. ÑQ'Ñ$Z. Ñ&Q'Ñ'B Z. ÔT# Ô"Z. Ô#
UÔ-UÕZ. ÕUÕC!Z. Ø7(Y  Ù 
Y-Ù,Y-Ú.\&Ú2([Û\&Û
[(Û%\&Û'[(Û(>\&)rÚ  rè   rÛ  rÜ  rÝ  rp   rÞ  rß  r   rj   rà  r8   rø   rù   )r  r!  r  r"  r)   rò   )r   r   r   r   Ú_FIRE_COOLDOWN_SECONDSr  Úpropertyr÷  r  r  r  r  r  r  r   r  r   r   r   rÙ  rÙ  Þ  s  € € € € € ðð ð $:ØNRØ26Ø(-ð	 ð  ð  ð  ð  ð> ð.ð .ð .ñ „Xð.ð(ð (ð (ð (ðTbð bð bð bð.ð ð ð ðð ð ð ðð ð ð ð$ð $ð $ð $ð,ð ,ð ,ð ,ðM&ð M&ð M&ð M&ð M&ð M&r   rÙ  zOptional[WakeWordDetector]Ú	_detectorzobject | NoneÚ_detector_ownerc                 ó.   — ddl m}   | ¦   «         dz  dz  S )Nr   ©Úget_default_hermes_rootÚruntimezwake-word.lock)r/  rB  rA  s    r   Ú
_lock_pathrD  ÿ  s.   € Ø8Ð8Ð8Ð8Ð8Ð8à"Ð"Ñ$Ô$ yÑ0Ð3CÑCÐCr   r1   c                óà  — | pt          ¦   «         }|j                             dd¬¦  «         t          |d¦  «        }	 t          j        dk    r©ddl}|                     dt          j        ¦  «         | 	                    ¦   «         dk    r)| 
                    d¦  «         |                     ¦   «          |                     d¦  «         |                     |                     ¦   «         |j        d¦  «         n9ddl}|                     |                     ¦   «         |j        |j        z  ¦  «         n=# t&          t(          f$ r)}|                     ¦   «          t-          d	¦  «        |‚d}~ww xY w|S )
zCAcquire the cross-process microphone lease, or raise WakeWordInUse.Tr5  za+bÚntr   Nó    r{   ú&Wake-word microphone is already owned.)rD  Úparentr>  rB  r0   r¦   ÚmsvcrtÚseekÚSEEK_ENDÚtellrw  ÚflushÚlockingÚfilenoÚLK_NBLCKÚfcntlÚflockÚLOCK_EXÚLOCK_NBr�   ÚBlockingIOErrorr÷   r   )r1   Ú	lock_pathÚhandlerJ  rR  rº   s         r   Ú_acquire_machine_lockrY    sK  € àÐ$�
™œ€IØÔ×Ò 4°$ÐÑ7Ô7Ð7Ý�)˜UÑ#Ô#€FðMÝŒ7�dŠ?ˆ?ØˆMˆMˆMà�KŠK˜�2œ;Ñ'Ô'Ð'Ø�{Š{‰}Œ} Ò!Ð!Ø—’˜UÑ#Ô#Ð#Ø—’‘”�Ø�KŠK˜‰NŒNˆNØ�NŠN˜6Ÿ=š=™?œ?¨F¬O¸QÑ?Ô?Ð?Ð?àˆLˆLˆLà�KŠK˜Ÿš™œ¨¬¸¼Ñ)FÑGÔGÐGøøÝ•_Ð%ð Mð Mð MØ�Š‰ŒˆÝÐDÑEÔEÈ1ÐLøøøøðMøøøð €Ms   ¾C2D1 Ä1E+Å$E&Å&E+rò   c                óž  — | €d S 	 t           j        dk    rHdd l}|                      d¦  «         |                     |                      ¦   «         |j        d¦  «         n1dd l}|                     |                      ¦   «         |j	        ¦  «         n# t          $ r Y nw xY w|                      ¦   «          d S # |                      ¦   «          w xY w)NrF  r   r{   )r0   r¦   rJ  rK  rO  rP  ÚLK_UNLCKrR  rS  ÚLOCK_UNr�   r÷   )rX  rJ  rR  s      r   Ú_release_machine_lockr]    sÄ   € Ø€~ØˆðÝŒ7�dŠ?ˆ?ØˆMˆMˆMà�KŠK˜‰NŒNˆNØ�NŠN˜6Ÿ=š=™?œ?¨F¬O¸QÑ?Ô?Ð?Ð?àˆLˆLˆLà�KŠK˜Ÿš™œ¨¬Ñ7Ô7Ð7øøÝð ð ð Øˆðøøøð 	�Š‰Œˆˆˆøˆ�Š‰Œˆˆøøøs*   †B	B ÂB6 Â
BÂB6 ÂBÂB6 Â6CÚdetectorc                ó  — t           5  t          | ur	 ddd¦  «         dS t          }dadada	 | j                             ¦   «          t          |¦  «         n# t          |¦  «         w xY w	 ddd¦  «         dS # 1 swxY w Y   dS )zDRelease ownership if the active microphone stream dies unexpectedly.N)Ú_detector_lockr>  Ú_detector_file_lockr?  rÚ  r÷   r]  )r^  Úlock_handles     r   Ú_detector_failedrc  1  sý   € õ 
ð 
/ð 
/Ý˜HÐ$Ð$Øð
/ð 
/ð 
/ñ 
/ô 
/ð 
/ð 
/ð 
/õ *ˆØˆ	ØˆØ"Ðð	/ØŒO×!Ò!Ñ#Ô#Ð#å! +Ñ.Ô.Ð.Ð.øÕ! +Ñ.Ô.Ð.Ð.øøøÐ.ð
/ð 
/ð 
/ñ 
/ô 
/ð 
/ð 
/ð 
/ð 
/ð 
/ð 
/ð 
/øøøð 
/ð 
/ð 
/ð 
/ð 
/ð 
/s-   ˆA9 A9®AÁA9ÁA(Á(A9Á9A=Â A=)Úconfigrà  rÛ  rÜ  ÚownerÚobjectrd  rà  c          	     ó¤  — |€t          d¦  «        ‚t          5  t          �Pt          |urt	          d¦  «        ‚| t          _        t                               ¦   «          t          cddd¦  «         S t          ¦   «         }	 |�|nt          ¦   «         }t          |¦  «        }t          || t          t          |¦  «        |¬¦  «        }|a|a|a|                     ¦   «          |cddd¦  «         S # t          $ rI t          �+	 t                               ¦   «          n# t          $ r Y nw xY wdadadat#          |¦  «         ‚ w xY w# 1 swxY w Y   dS )a/  Claim, build, and start the detector. Idempotent for the same owner.

    Raises if engine construction fails (missing deps / access key / model);
    callers should probe :func:`check_wake_word_requirements` first. A different
    owner, including another process, receives :class:`WakeWordInUse`.
    Nz wake-word owner must not be NonerH  )rÞ  r   rà  )ru   r`  r>  r?  r   rÛ  r  rY  ra   rª  rÙ  rc  ro   ra  r  r`   r  r]  )rÛ  re  rd  rà  rb  rB   rÚ  r^  s           r   Ústart_listeningrh  A  sé  € ð €}ÝÐ;Ñ<Ô<Ð<õ 
ð !ð !ÝÐ Ý eÐ+Ð+Ý#Ð$LÑMÔMÐMØ '�IÔÝ×ÒÑÔÐÝð!ð !ð !ð !ñ !ô !ð !ð !õ ,Ñ-Ô-ˆð	Ø"Ð.�&�&Õ4IÑ4KÔ4KˆCÝ" 3Ñ'Ô'ˆFÝ'ØØÝ+Ý*¨3Ñ/Ô/Ø-ðñ ô ˆHð !ˆIØ#ˆOØ"-ÐØ�NŠNÑÔÐØð-!ð !ð !ð !ñ !ô !ð !ð !øõ. ð 
	ð 
	ð 
	ÝÐ$ðÝ—N’NÑ$Ô$Ð$Ð$øÝ ð ð ð Ø�DðøøøàˆIØ"ˆOØ"&ÐÝ! +Ñ.Ô.Ð.Øð
	øøøð/!ð !ð !ð !øøøð !ð !ð !ð !ð !ð !sO   ™AEÁ1EÂ A"C/Ã/EÄDÄEÄ
D(Ä%EÄ'D(Ä(EÅEÅE	ÅE	c                óh   — t           5  t          d uot          | u cd d d ¦  «         S # 1 swxY w Y   d S rd   )r`  r>  r?  ©re  s    r   Úowns_listenerrk  v  s’   € Ý	ð Bð BÝ Ð$ÐA­¸EÐ)AðBð Bð Bð Bñ Bô Bð Bð Bð Bð Bð Bð Bøøøð Bð Bð Bð Bð Bð Bs   ˆ'§+®+c                ó¶   — t           5  t          �	t          | ur	 ddd¦  «         dS t                               ¦   «          	 ddd¦  «         dS # 1 swxY w Y   dS )z;Release the microphone only when ``owner`` holds the lease.NFT)r`  r>  r?  r  rj  s    r   Úpause_listeningrm  {  s¼   € å	ð ð ÝÐ¥°uÐ <Ð <Øðð ð ñ ô ð ð ð õ 	�ŠÑÔÐØð	ð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð ó   ˆA§AÁAÁAc                ó¶   — t           5  t          �	t          | ur	 ddd¦  «         dS t                               ¦   «          	 ddd¦  «         dS # 1 swxY w Y   dS )z;Re-open the microphone only when ``owner`` holds the lease.NFT)r`  r>  r?  r  rj  s    r   Úresume_listeningrp  „  s¾   € å	ð ð ÝÐ¥°uÐ <Ð <Øðð ð ñ ô ð ð ð õ 	×ÒÑÔÐØð	ð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð rn  c                ó  — t           5  t          �	t          | ur	 ddd¦  «         dS t          }t          }dadada	 |                     ¦   «          t          |¦  «         n# t          |¦  «         w xY w	 ddd¦  «         dS # 1 swxY w Y   dS )z<Fully stop the detector only when ``owner`` holds the lease.NFT)r`  r>  r?  ra  r  r]  )re  Údetrb  s      r   Ústop_listeningrs  �  s   € õ 
ð ð ÝÐ¥°uÐ <Ð <Øðð ð ñ ô ð ð ð õ ˆÝ)ˆØˆ	ØˆØ"Ðð	/Ø�HŠH‰JŒJˆJå! +Ñ.Ô.Ð.Ð.øÕ! +Ñ.Ô.Ð.Ð.øøøØðð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð s-   ˆB§B¼A ÁBÁ A1Á1BÂBÂ	Bc                 ód   — t           5  t          } d d d ¦  «         n# 1 swxY w Y   | d uo| j        S rd   )r`  r>  r÷  ©rr  s    r   Úis_listeningrv  Ÿ  ss   € Ý	ð ð Ýˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð à�dˆ?Ð*˜sœ{Ð*ó   ˆœ £ c                 ód   — t           5  t          } ddd¦  «         n# 1 swxY w Y   | duo| j        S )a  True when the armed stream has delivered only silence (dead mic).

    The stream opens fine but every frame is zeros, so detection can never
    fire. Lets status surfaces show "listening but the microphone appears
    silent" instead of a healthy state.
    N)r`  r>  rð  ru  s    r   Úaudio_is_silentry  ¥  sv   € õ 
ð ð Ýˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð à�dˆ?Ð/˜sÔ/Ð/rw  c                ób  — t           5  t          }ddd¦  «         n# 1 swxY w Y   |�t          |j        ¦  «        S | �| nt	          ¦   «         } t          | ¦  «        }	 t          ¦   «         \  }}n0# t          t          f$ r}|t          |¦  «        dœcY d}~S d}~ww xY wt          ||¦  «        S )zDReturn configured/active PortAudio input diagnostics for status UIs.N)r²   r´   )r`  r>  rG   rä  ra   ro   rŽ   rQ   r�   r(   r½   )rB   rr  r²   r’   r“   rº   s         r   Úget_input_device_statusr{  ±  s  € å	ð ð Ýˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð à
€Ý�CÔ,Ñ-Ô-Ð-à�ˆ#ˆ#Õ&;Ñ&=Ô&=€CÝ˜SÑ!Ô!€Hð7Ý‘”‰ˆˆAˆAøÝ�Ð!ð 7ð 7ð 7Ø$­s°1©v¬vÐ6Ð6Ð6Ð6Ð6Ð6Ð6Ð6øøøøð7øøøå! " hÑ/Ô/Ð/s*   ˆœ £ ÁA1 Á1BÂBÂBÂBrë   c                 ó‚   — t           5  t          } ddd¦  «         n# 1 swxY w Y   | €dS t          | j        dd¦  «        S )z”(matched phrase, profile) of the most recent wake fire, if the engine
    reports per-phrase matches (sherpa multi-profile routing). None otherwise.Nrì   )r`  r>  r‘   rÚ  ru  s    r   Úget_last_matchr}  Á  s‚   € õ 
ð ð Ýˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð à
€{ØˆtÝ�3”:˜|¨TÑ2Ô2Ð2rw  c                óì   — t           5  t          �	t          | ur	 ddd¦  «         dS t          j        s	 ddd¦  «         dS t          }ddd¦  «         n# 1 swxY w Y   |                     |¦  «         dS )zšPush client-captured PCM into the armed detector (client capture mode).

    Returns True when the frame was accepted for ``owner``'s armed detector.
    NFT)r`  r>  r?  rà  r  )re  r   rr  s      r   Ú
feed_audior  Ë  sò   € õ
 
ð ð ÝÐ¥°uÐ <Ð <Øðð ð ñ ô ð ð ð õ Ô'ð 	Øð	ð ð ñ ô ð ð ð õ
 ˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð ‡H‚HˆYÑÔÐØˆ4s   ˆA§AÁAÁAÁAc                 óæ   — t           5  t          } ddd¦  «         n# 1 swxY w Y   | €
t          ddœS t          t          t	          | j        dd¦  «        pd¦  «        t          | j        ¦  «        dœS )z8Sample rate + frame length for client capture streamers.Nré   )Úsample_raterê   rê   )r�  rê   rà  )r`  r>  rÆ   rl   r‘   rÚ  r8   rà  ru  s    r   Údetector_frame_infor‚  Ú  s³   € å	ð ð Ýˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð à
€{Ý*¸DÐAÐAÐAå"Ý�G C¤J°ÀÑEÔEÐMÈÑNÔNÝ˜sÔ1Ñ2Ô2ðð ð rw  )r&   )r'   r(   r)   r(   rø   )r)   r(   )rB   r!   r)   r(   )r)   r!   )rB   r!   rb   r(   r)   r   )rB   r!   r)   rj   )rB   r!   r)   rp   )rB   r!   r)   rl   rd   )rB   r   r)   r(   )rB   r   r‚   r8   rƒ   r8   r)   r(   )r   r(   rB   r   r)   r8   )r)   r¡   )r²   rj   r)   r!   )r¹   r!   r)   r(   )r¹   r!   r)   rl   )rÈ   rl   )rn   r(   r)   r8   )r)   r   )r1  r2  r)   r   )rB   r!   r)   rè   )rB   r   r)   r!   )r1   r2  rù   )r^  rÙ  r)   rò   )
rÛ  rÜ  re  rf  rd  r   rà  r8   r)   rÙ  )re  rf  r)   r8   )r)   rë   )Zr   Ú
__future__r   Úloggingr0   r=   ræ  r2  Úpathlibr   Útypingr   r   r   r   Ú	getLoggerr   rI   rÆ   r<  r  r|   r1  r,  r  r   r"   rú   r5   Ú	frozensetr  r7   r?   rA   rH   rL   r[   ra   rf   ri   ro   ry   r~   r�   rŠ   r‰   rš   r    rª   rŽ   r±   r½   rÂ   rÇ   rã   ræ   rè   rÿ   r  rA  r;  r0  rI  rK  r’  rª  r²  r¾  r×  rÙ  r>  r?  ra  rë  r`  rD  rY  r]  rc  rh  rk  rm  rp  rs  rv  ry  r{  r}  r  r‚  r   r   r   ú<module>r‰     s°  ððð ð ð> #Ð "Ð "Ð "Ð "Ð "à €€€Ø 	€	€	€	Ø 
€
€
€
Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð Ø 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0à	ˆÔ	˜8Ñ	$Ô	$€ð €ð Ð ØÐ ð  !Ð ð €ØÐ ðNð Nð Nð Nð N�Lñ Nô Nð Nð ØØð
 ØØØØ7Øðð €	ð ð ð ñ ð$ #Ð Ø"˜Ð#MÐ#MÐ#MÑNÔNÐ ð`ð `ð `ð `ð `ðFð Fð Fð Fð5ð 5ð 5ð 5ð Ð ðð ð ð ðFð ð ð ð<0ð 0ð 0ð 0ð6ð 6ð 6ð 6ð
Hð Hð Hð Hðð ð ð ð!ð !ð !ð !ðð ð ð ð4ð 4ð 4ð 4ð 4ð %)ðð  Øð	ð ð ð ð ð ðDð ð ð ð<=ð =ð =ð =ð =ð$ð ð ð ðð ð ð ðLð ð ðð ð ð ð"ð "ð "ð "ðJ8ð 8ð 8ð 8ðð ð ð ð@ð @ð @ð @ð.ð ð ð ð0ð ð ð ð ñ ô ð ð,ð ð ð ðYð Yð Yð Yð Y˜'ñ Yô Yð YðBCð ð OÐ ð5ð 5ð 5ð 5ðð ð ð ð ð*uð uð uð uð u�wñ uô uð uðp*ð *ð *ð *ð *�wñ *ô *ð *ðZBð Bð Bð Bðð ð ð ð )ð )ð )ð )ðXTð Tð Tð Tð TðvT&ð T&ð T&ð T&ð T&ñ T&ô T&ð T&ðv )-€	Ð ,Ð ,Ð ,Ñ ,Ø!%€Ð %Ð %Ð %Ñ %ØÐ Ø�”Ñ!Ô!€ðDð Dð Dð Dðð ð ð ð ð2ð ð ð ð&/ð /ð /ð /ð( (,Ø ð2ð 2ð 2ð 2ð 2ð 2ðjBð Bð Bð Bð
ð ð ð ðð ð ð ðð ð ð ð$+ð +ð +ð +ð	0ð 	0ð 	0ð 	0ð0ð 0ð 0ð 0ð 0ð 3ð 3ð 3ð 3ðð ð ð ð
ð 
ð 
ð 
ð 
ð 
r   