
    |+jK                       U d Z ddl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 ddlmZ ddlmZ ddlmZ ddlmZmZmZ  ej        e          Zdvd	ZddlmZ d
edefdZ  e d          Z! e d          Z" e d          Z#dZ$dZ%dZ& ej'        dd          Z( ej'        dd          Z) ej'        dd          Z* ej'        dd          Z+dZ,dZ-dZ. ej'        dd          Z/ ej'        d d!          Z0 ej'        d"d#          Z1 ej'        d$d%          Z2h d&Z3h d'Z4d(Z5h d)Z6h d*Z7da8e
e9         e:d+<   da;e
e         e:d,<   de<fd-Z=dvd.e
e<         defd/Z>defd0Z?d1ede
e         fd2Z@de
e         fd3ZAde
e         fd4ZBde
e         fd5ZCdefd6ZDd7e
e         defd8ZEd7e
e         defd9ZFdefd:ZG eHh d;          ZId<ZJdZKd=ZL eHh d>          ZMd.eeef         d?edeeef         fd@ZNd.eeef         d?edeeef         fdAZOdBeeef         defdCZPdDed.eeef         de
eeef                  fdEZQd.eeef         fdFZRdvd.e
eeef                  defdGZSdBeeef         deTfdHZUdBeeef         defdIZVdJedKeWde
e         fdLZXdMedNe
e         defdOZYdJedPeeef         defdQZZdRej[        ddfdSZ\dTedUeTdej]        fdVZ^dWedXedYedefdZZ_	 dvd[ed\edBeeef         d.eeef         d]e
e         deeef         fd^Z`d.e<defd_Za	 dvddd`d[edDed.e
eeef                  dae
e         dbe
e         de
eeef                  fdcZbd[ede
eeef                  fddZcdeZddfeedefdgZfd7efdhZgd[ed7edeeef         fdiZhd[edjedeie
e         e
e         f         fdkZjd[ed7edeeef         fdlZkd[ed7edeeef         fdmZld[ed7edeeef         fdnZmd[ed7edeeef         fdoZnd[ed7edeeef         fdpZod[ed7edeeef         fdqZpdvd[edae
e         deeef         fdrZqdeieef         fdsZrdtedefduZsdS )wu?  
Transcription Tools Module

Provides speech-to-text transcription with six providers:

  - **local** (default, free) — faster-whisper running locally, no API key needed.
    Auto-downloads the model (~150 MB for ``base``) on first use.
  - **groq** (free tier) — Groq Whisper API, requires ``GROQ_API_KEY``.
  - **openai** (paid) — OpenAI Whisper API, requires ``VOICE_TOOLS_OPENAI_KEY``.
  - **mistral** — Mistral Voxtral Transcribe API, requires ``MISTRAL_API_KEY``.
  - **xai** — xAI Grok STT API, requires ``XAI_API_KEY``. High accuracy,
    Inverse Text Normalization, diarization, 21 languages.
  - **elevenlabs** — ElevenLabs Scribe API, requires ``ELEVENLABS_API_KEY``.

Used by the messaging gateway to automatically transcribe voice messages
sent by users on Telegram, Discord, WhatsApp, Slack, and Signal.

Supported input formats: mp3, mp4, mpeg, mpga, m4a, wav, webm, ogg, aac

Usage::

    from tools.transcription_tools import transcribe_audio

    result = transcribe_audio("/path/to/audio.ogg")
    if result["success"]:
        print(result["transcript"])
    N)Path)OptionalDictAny)urljoin)is_truthy_value)resolve_managed_tool_gateway)managed_nous_tools_enabled%nous_tool_gateway_unavailable_messageresolve_openai_audio_api_keyc                 ~    	 ddl m} n%# t          $ r t          j        | |          cY S w xY w ||           }||n|S )a  Read env values through the live config module.

    Tests may monkeypatch and later restore ``hermes_cli.config.get_env_value``
    before this module is imported. Resolve the helper at call time so STT does
    not keep a stale imported function for the rest of the test process.
    r   )get_env_value)hermes_cli.configr   ImportErrorosgetenv)namedefault_get_env_valuevalues       8/usr/local/lib/hermes-agent/tools/transcription_tools.pyr   r   2   sl    (EEEEEEE ( ( (yw'''''(N4  Em77.s   	 ++module_namereturnc                     	 t          j        |           d uS # t          t          f$ r& | t	                      v p| t
          j        j        v cY S w xY wN)_ilu	find_specr   
ValueErrorglobalsr   sysmodules)r   s    r   _safe_find_specr"   G   sd    I~k**$66$ I I Igii'H;"&.+HHHHIs    4AAfaster_whisperopenai	mistralailocalbaseenSTT_OPENAI_MODEL	whisper-1STT_GROQ_MODELwhisper-large-v3-turboSTT_MISTRAL_MODELzvoxtral-mini-latestSTT_ELEVENLABS_MODEL	scribe_v2HERMES_LOCAL_STT_COMMANDHERMES_LOCAL_STT_LANGUAGE)z/opt/homebrew/binz/usr/local/binGROQ_BASE_URLzhttps://api.groq.com/openai/v1STT_OPENAI_BASE_URLzhttps://api.openai.com/v1XAI_STT_BASE_URLzhttps://api.x.ai/v1ELEVENLABS_STT_BASE_URLzhttps://api.elevenlabs.io/v1>
   .aac.m4a.mp3.mp4.ogg.flac.mpeg.mpga.webm.wav>   .aif.aiffr?   i  >   gpt-4o-transcribegpt-4o-mini-transcriber*   >   whisper-large-v3distil-whisper-large-v3-enr,   _local_model_local_model_namec                  p    	 ddl m}   |                                 di           S # t          $ r i cY S w xY w)zDLoad the ``stt`` section from user config, falling back to defaults.r   load_configstt)r   rJ   get	ExceptionrI   s    r   _load_stt_configrN   x   sY    111111{}}  +++   			s   #& 55
stt_configc                 p    | t                      } |                     dd          }t          |d          S )z(Return whether STT is enabled in config.NenabledT)r   )rN   rL   r   )rO   rQ   s     r   is_stt_enabledrR      s9    %''
nnY--G7D1111    c                  F    	 t                       dS # t          $ r Y dS w xY w)zbReturn True when OpenAI audio can use config credentials, env credentials, or the managed gateway.TF)#_resolve_openai_audio_client_configr    rS   r   _has_openai_audio_backendrW      s:    +---t   uus    
  binary_namec                     t           D ]X}t          |          | z  }|                                r0t          j        |t          j                  rt          |          c S Yt          j        |           S )zMFind a local binary, checking common Homebrew/local prefixes as well as PATH.)	COMMON_LOCAL_BIN_DIRSr   existsr   accessX_OKstrshutilwhich)rX   	directory	candidates      r   _find_binaryrc      sm    * " "	OOk1	 	"")Irw"?"? 	"y>>!!!<$$$rS   c                       t          d          S )Nffmpegrc   rV   rS   r   _find_ffmpeg_binaryrg      s    !!!rS   c                       t          d          S )Nwhisperrf   rV   rS   r   _find_whisper_binaryrj      s    	"""rS   c                      t          j        t          d                                          } | r| S t	                      }|rt          j        |          }| dS d S )N za {input_path} --model {model} --output_format txt --output_dir {output_dir} --language {language})r   r   LOCAL_STT_COMMAND_ENVstriprj   shlexquote)
configuredwhisper_binaryquoted_binarys      r   _get_local_command_templatert      sn    0"55;;==J )++N 
N33 > > >	
 4rS   c                  "    t                      d uS r   )rt   rV   rS   r   _has_local_commandrv      s    &((44rS   
model_namec                     | r| t           v s	| t          v r<| r3| t           v s	| t          v r!t                              d| t                     t          S | S )a  Return a valid faster-whisper model size, mapping cloud-only names to the default.

    Cloud providers like OpenAI use names such as ``whisper-1`` which are not
    valid for faster-whisper (which expects ``tiny``, ``base``, ``small``,
    ``medium``, or ``large-v*``).  When such a name is detected we fall back to
    the default local model and emit a warning so the user knows what happened.
    zSTT model '%s' is a cloud-only name and cannot be used with the local provider. Falling back to '%s'. Set stt.local.model to a valid faster-whisper size (tiny, base, small, medium, large-v3).)OPENAI_MODELSGROQ_MODELSloggerwarningDEFAULT_LOCAL_MODELrw   s    r   _normalize_local_modelr      sn      	#}44
k8Q8Q 	:66*:S:SNNM #   #"rS   c                      t          |           S r   )r   r~   s    r   _normalize_local_command_modelr      s    !*---rS   c                      	 ddl m}   | dd           ddlm} |                    d          rdS n2# t
          $ r%}t                              d	|           Y d}~nd}~ww xY wdS )
ao  Attempt to lazy-install faster-whisper and return True on success.

    The module-level ``_HAS_FASTER_WHISPER`` flag is set at import time and
    cached. If the package wasn't installed at startup, calling ``ensure()``
    installs it. This function re-checks dynamically after installation so
    the provider can use it immediately without a process restart.
    r   ensurezstt.faster_whisperFpromptNr#   Tz)Lazy install of faster-whisper failed: %s)tools.lazy_depsr   importlib.utilutilr   rM   r{   debug)r   _iuexcs      r   _try_lazy_install_sttr      s    G******
 	#E2222$$$$$$==)** 	4	 G G G@#FFFFFFFFG5s   .3 
A"AA">   xaigroqr&   r$   mistrallocal_command,  txt>   srtr   vttjsonr   c                     t          | t                    si S |                     |          }t          |t                    r|ni S )z=Return an stt sub-section if it's a dict, else an empty dict.)
isinstancedictrL   )rO   r   sections      r   _get_stt_sectionr     sC    j$'' 	nnT""G $//777R7rS   c                 
   t          | d          }t          |t                    r|                    |          nd}t          |t                    r|S |                                t
          vrt          | |          }|r|S i S )u  Return the config dict for a user-declared STT command provider.

    Looks up ``stt.providers.<name>`` first (the canonical location), and
    falls back to ``stt.<name>`` so users who followed the built-in layout
    still work. Returns an empty dict when the provider is not declared.

    Built-in names are NOT special-cased here — the caller short-circuits
    them before this is consulted, AND ``_is_command_stt_provider_config``
    requires an explicit ``command:`` value, so a built-in section like
    ``stt.openai`` (which has ``model``/``language`` but no ``command``)
    can't accidentally be treated as a command provider.
    	providersN)r   r   r   rL   lowerBUILTIN_STT_PROVIDERS)rO   r   r   r   legacys        r   _get_named_stt_provider_configr     s      ![99I%/	4%@%@JimmD!!!dG'4    zz||000!*d33 	MIrS   configc                 j   t          | t                    sdS t          |                     d          pd                                                                          }|r|dk    rdS |                     d          }t          |t                    o t          |                                          S )z?Return True when *config* declares a command-type STT provider.Ftyperl   command)r   r   r^   rL   rn   r   bool)r   ptyper   s      r   _is_command_stt_provider_configr   7  s    fd## u

6""(b))//117799E )##ujj##Ggs##=W]]__(=(==rS   providerc                     | sdS |                                                                  }|t          v s|dk    rdS t          ||          }t	          |          r|S dS )zReturn the provider config if *provider* resolves to a command type.

    Built-in provider names are rejected (they have native handlers).
    Returns None when the name is a built-in, ``"none"``, unknown, or not
    a command type.
    Nnone)r   rn   r   r   r   )r   rO   keyr   s       r   $_resolve_command_stt_provider_configr   B  sn      t
..


 
 
"
"C
###sf}}t+J<<F&v.. 4rS   c              #     K   t          | t                    sdS t          | d          }|pi                                 D ]J\  }}t          |t                    r0|                                t          vrt          |          r||fV  KdS )zHYield (name, config) pairs for every declared command-type STT provider.Nr   )r   r   r   itemsr^   r   r   r   )rO   r   r   cfgs       r   _iter_command_stt_providersr   W  s      j$''  [99Io2,,..    	cdC   	 TZZ\\9N%N%N.s33  Ci   rS   c                 T    | t                      } t          |           D ]\  }} dS dS )z=Return True when any command-type STT provider is configured.NTF)rN   r   )rO   _name_cfgs      r   _has_any_command_stt_providerr   b  s:    %''
2:>>  ttt5rS   c                    |                      d|                      dt                              }	 t          |          }n+# t          t          f$ r t          t                    cY S w xY w|dk    rt          t                    S |S )z5Return timeout in seconds, falling back when invalid.timeouttimeout_secondsr   )rL   #DEFAULT_COMMAND_STT_TIMEOUT_SECONDSfloat	TypeErrorr   )r   rawr   s      r   _get_command_stt_timeoutr   k  s    
**Y

+<>a b b
c
cC:c

z" : : :899999:zz8999Ls   A %A)(A)c                    |                      d          p|                      d          pt          }t          |                                                                                              d          }|t          v r|nt          S )z6Return the validated output format (txt/json/srt/vtt).formatoutput_format.)rL   !DEFAULT_COMMAND_STT_OUTPUT_FORMATr^   r   rn   lstripCOMMAND_STT_OUTPUT_FORMATS)r   r   fmts      r   _get_command_stt_output_formatr   w  s{     	

8 	-::o&&	-, 
 c((..


 
 
"
"
)
)#
.
.C333339ZZrS   command_templatepositionc                     d}d}d}||k     r\| |         }|dk    r	|dk    rd}n:|dk    r|rd}n/|dk    rd}n&|dk    rd}n|dk    rd}n|dk    rd}n|dk    r|dz  }|dz  }||k     \|S )	u  Return the shell quote character active right before *position*.

    Mirrors ``tools.tts_tool._shell_quote_context`` — kept local to avoid
    cross-module import of a private helper. Returns ``"'"`` / ``'"'`` when
    inside a quoted region, ``None`` for bare context.
    NFr   '"\T   rV   )r   r   rp   escapedichars         r   _shell_quote_context_sttr     s      EG	A
h,,"C<<s{{c\\ S[[EES[[EET\\FA	Q% h,,& LrS   r   quote_contextc                 \   |dk    r|                      dd          S |dk    rR|                      dd                               dd                               dd                               d	d
          S t          j        dk    rt          j        | g          S t          j        |           S )zQuote a placeholder value for its position in a shell command template.

    Mirrors ``tools.tts_tool._quote_command_tts_placeholder``.
    r   z'\''r   r   z\\z\"$z\$`z\`nt)replacer   r   
subprocesslist2cmdlinero   rp   )r   r   s     r   _quote_command_stt_placeholderr     s    
 }}S'***WT6""WS%  WS%  WS%  	
 
w$&w///;urS   placeholdersc                 r   	 ddl d                    fdD                       }                    d| d| d          }g 	dd	d
t          f 	fd}|                    |           }|                    dd                              dd          }	D ]\  }}|                    ||          }|S )a  Replace supported placeholders while preserving ``{{`` / ``}}``.

    Mirrors ``tools.tts_tool._render_command_tts_template``. Placeholders
    are shell-quote-aware: ``{voice}`` inside single quotes gets
    single-quote-safe escaping, inside double quotes gets ``$``/`` ` ``/`` " ``
    escaping, outside quotes gets ``shlex.quote``. Doubled braces ``{{`` and
    ``}}`` are preserved as literal ``{`` / ``}`` for users who want to
    embed JSON snippets in their command.
    r   N|c              3   B   K   | ]}                     |          V  d S r   )escape).0r   res     r   	<genexpr>z/_render_command_stt_template.<locals>.<genexpr>  s-      >>RYYt__>>>>>>rS   z(?<!\$)(?:\{\{(?P<double>z)\}\}|\{(?P<single>z)\})matchzre.Match[str]r   c                    |                      d          p|                      d          }dt                     d}                    |t          |         t	          |                                                     f           |S )Ndoublesingle__HERMES_STT_PLACEHOLDER___)grouplenappendr   r   start)r   r   tokenr   r   replacementss      r   replace_matchz3_render_command_stt_template.<locals>.replace_match  s    {{8$$=H(=(=AC,=,=AAA*T"()95;;==II 
 	 	 	 rS   z{{{z}}})r   joincompiler^   subr   )
r   r   namespatternr   renderedr   r   r   r   s
   ``      @@r   _render_command_stt_templater     s    IIIHH>>>>>>>>>EjjPuPPEPPP G +-L
_ 
 
 
 
 
 
 
 
 
 {{=*:;;Hc**224==H$ 2 2u##E511OrS   procc           	         |                                  dS t          j        dk    rv	 t          j        ddddt          | j                  gt          j        t          j        dt          j                   n$# t          $ r | 	                                 Y nw xY wdS 	 d	dl
}nf# t          $ rY |                                  	 |                     d
           n)# t          j        $ r | 	                                 Y nw xY wY dS w xY w	 |                    | j                  }|                    d          D ]'}	 |                                 # |j        $ r Y $w xY w|                                 n0# |j        $ r Y dS t          $ r |                                  Y nw xY w	 |                     d
           dS # t          j        $ r Y nw xY w	 |                    | j                  }|                    d          D ]'}	 |	                                 # |j        $ r Y $w xY w|	                                 dS # |j        $ r Y dS t          $ r | 	                                 Y dS w xY w)zBest-effort termination of a shell process and all of its children.

    Mirrors ``tools.tts_tool._terminate_command_tts_process_tree``.
    Nr   taskkillz/Fz/Tz/PID   )stdoutstderrr   stdinr      r   T)	recursive)pollr   r   r   runr^   pidDEVNULLrM   killpsutilr   	terminatewaitTimeoutExpiredProcesschildrenNoSuchProcess)r   r  parentchilds       r   #_terminate_command_stt_process_treer    s
   
 yy{{	w$		NT4TX?!)!) (      	 	 	IIKKKKK		   	IIaI    ( 	 	 	IIKKKKK	))__t_44 	 	E!!!!'         		!	$   ))__t_44 	 	E

'         		s   AA7 7BBB# #DCD#C?<D>C??DD
2E7 =EE7 
EE7 EE7 7
F$F$#F$(G   GG2I 	HI 
H+(I *H++I 
I2I21I2r   r   c                    dt           j        t           j        dd}t          j        dk    rt	          t           dd          |d<   nd|d<   t          j        | fi |dt           j        i}	 |                    |	          \  }}n# t           j        $ rz}t          |           	 |                    d
	          \  }}n2# t          $ r% t	          |dd          }t	          |dd          }Y nw xY wt          j        | |||          |d}~ww xY w|j        rt          j        |j        | ||          t          j        | |j        ||          S )z~Run a command-provider shell command with process-tree timeout cleanup.

    Mirrors ``tools.tts_tool._run_command_tts``.
    T)shellr   r   textr   CREATE_NEW_PROCESS_GROUPr   creationflagsstart_new_sessionr   r  r   outputNr   )r  r   )r   PIPEr   r   getattrPopenr  communicater  r  rM   
returncodeCalledProcessErrorCompletedProcess)r   r   popen_kwargsr   r   r   r   s          r   _run_command_sttr!    s    //	$ $L 
w$(/
<VXY(Z(Z_%%,0()GNN|NN:;MNNND))')::$   +D111	2!--a-88NFFF 	2 	2 	2S(D11FS(D11FFF	2 '	
 
 

 	  
+O	
 
 
 	
 &wPPPs<   ,B DD
%B?>D
?,C.+D
-C..D

Doutput_pathr   r   c                    |                                  ry	 |                     d                                          }nK# t          $ r> |                                                     dd                                          }Y nw xY w|r|S |r(|                                r|                                S t          d|  d          )u  Return the transcript text from a command-provider invocation.

    Resolution:
      1. If ``output_path`` exists and is non-empty → read it (raw text).
      2. Else if ``stdout`` is non-empty → use stdout (lets users write
         curl-style one-liners that emit transcript to stdout instead of
         writing a file).
      3. Else → raise RuntimeError (no usable output produced).

    For JSON format, we still return the raw bytes — extracting a
    ``text`` field is out of scope; users either configure ``format: txt``
    or post-process JSON downstream. (Same trade-off as TTS: the runner
    doesn't try to be clever about output shape.)
    utf-8encodingr   )errorsz-Command STT provider wrote no output file at z and produced no stdout)r[   	read_textrn   UnicodeDecodeError
read_bytesdecodeRuntimeError)r"  r   r   contents       r   _read_command_stt_outputr.  J  s      	Y!++W+==CCEEGG! 	Y 	Y 	Y!,,..55gi5PPVVXXGGG	Y 	N &,,.. ||~~
	" 	" 	" 	"  s   (? ABB	file_pathprovider_namemodel_overridec                    t          |                    d          pd                                          }|sdd|d| ddS t          |                                           }|                                s
dd|d|  dS t          |          }t          |          }|                    d          p|                    d          pt          }	|p|                    d	          pd}
	 t          j
        d
| d          5 }t          |          d| z  }t          |                                          t          |          t          |j                  |t          |	          t          |
          d}t          ||          }t                              d|j        |           	 t#          ||          }n# t$          j        $ r dd|d| d|dddcY cddd           S t$          j        $ r}g }|j        r/|                    d|j                                                    |j        r/|                    d|j                                                    d                    |          pd}dd|d| d|j         d| dcY d}~cddd           S d}~ww xY w	 t5          ||j        pd|          }n7# t6          $ r*}dd|t          |          dcY d}~cddd           S d}~ww xY w	 ddd           n# 1 swxY w Y   n$# t8          $ r}dd|d| d| dcY d}~S d}~ww xY wt                              d|j        |t;          |                     d||dS )a,  Transcribe via a user-declared ``stt.providers.<name>: type: command``.

    Placeholder grammar:

    | Placeholder       | Substituted with                                          |
    |-------------------|-----------------------------------------------------------|
    | ``{input_path}``  | absolute path to the audio file (original location)       |
    | ``{output_path}`` | absolute path the provider should write its transcript to |
    | ``{output_dir}``  | parent dir of ``{output_path}``                           |
    | ``{format}``      | configured output format (``txt`` / ``json`` / ``srt`` / ``vtt``) |
    | ``{language}``    | configured language code (default ``en``)                 |
    | ``{model}``       | configured model id (empty when not set)                  |

    All placeholders are shell-quote-aware (see ``_render_command_stt_template``).
    Doubled braces ``{{`` and ``}}`` are preserved as literal braces.

    Returns the standard transcribe-response envelope (``success``,
    ``transcript``, ``provider``, ``error``).
    r   rl   Fzstt.providers.z.command is not configured)success
transcriptr   errorAudio file not found: languagemodelzhermes-cmd-stt--prefixztranscript.)
input_pathr"  
output_dirr   r7  r8  z0Transcribing %s via command STT provider '%s'...zSTT command provider 'z' timed out after gsNzstderr: zstdout: z; zno command outputz' exited with code z: z
' failed: z7Transcribed %s via command STT provider '%s' (%d chars)Tr3  r4  r   )r^   rL   rn   r   
expanduserr[   r   r   DEFAULT_COMMAND_STT_LANGUAGEtempfileTemporaryDirectoryresolver  r   r{   infor   r!  r   r  r  r   r   r   r   r  r.  r,  OSErrorr   )r/  r0  r   rO   r1  r   audior   r   r7  r8  tmpdirr"  r   r   resultr   detail_partsdetailtranscript_texts                       r   _transcribe_command_sttrN  h  s<   4 6::i006B77==?? 
%OmOOO	
 
 	
 OO&&((E<<>> 
%9i99	
 
 	
 'v..G26::M

: 	(>>*%%	(' 
 7fjj117RE?
(0R-0R0R0RSSS 6	W]v,,)F})F)FFK!%--//22";//!+"455'MMU L 33C\RRGKKB
M  )'7;;, 	 	 	$"$ -( ( ("( ( (   %6	 6	 6	 6	 6	 6	 6	 6	6 0   !: I ''(G3:3C3C3E3E(G(GHHH: I ''(G3:3C3C3E3E(G(GHHH<00G4G$"$ -6 6 6>6 6-36 6      E6	 6	 6	 6	 6	 6	 6	 6	6"
":!4"m# #     $"$ - XX	      c6	 6	 6	 6	 6	 6	 6	 6	`  [6	 6	 6	 6	 6	 6	 6	 6	 6	 6	 6	 6	 6	 6	 6	p  
 
 
%LmLLsLL	
 
 	
 	
 	
 	
 	
 	

 KKA
M3#7#7  
 %!  s   ,L B(L
.F?>L
?J)L
L +J)9BJ$J)L
L $J))L
-KL

K:K5#K:$L
(L 5K::L
>L 
LL LL 
L7 L2,L72L7c                 r   t          |           sdS d| v }|                     dt                    }|r|dk    rEt          rdS t	                      rdS t                      rdS t                              d           dS |dk    rOt	                      rdS t          rt                              d           dS t                              d           dS |dk    r4t          rt          d	          rdS t                              d
           dS |dk    r3t          rt                      rdS t                              d           dS |dk    r4t          rt          d          rdS t                              d           dS |dk    rAddlm}  |                                d          rdS t                              d           dS |dk    r-t          d          rdS t                              d           dS |S t          rdS t	                      rdS t                      rdS t          r+t          d	          rt                              d           dS t          r*t                      rt                              d           dS t          r+t          d          rt                              d           dS 	 ddlm}  |                                d          rt                              d           dS n# t          $ r Y nw xY wt          d          rt                              d           dS dS )u   Determine which STT provider to use.

    When ``stt.provider`` is explicitly set in config, that choice is
    honoured — no silent cloud fallback.  When no provider is configured,
    auto-detect tries: local > groq (free) > openai (paid).
    r   r   r&   r   zhSTT provider 'local' configured but unavailable (install faster-whisper or set HERMES_LOCAL_STT_COMMAND)z9Local STT command unavailable, using local faster-whisperz7STT provider 'local_command' configured but unavailabler   GROQ_API_KEYz7STT provider 'groq' configured but GROQ_API_KEY not setr$   z9STT provider 'openai' configured but no API key availabler   MISTRAL_API_KEYz`STT provider 'mistral' configured but mistralai package not installed or MISTRAL_API_KEY not setr   r   resolve_xai_http_credentialsapi_keyzBSTT provider 'xai' configured but no xAI credentials are available
elevenlabsELEVENLABS_API_KEYzCSTT provider 'elevenlabs' configured but ELEVENLABS_API_KEY not setz.No local STT available, using Groq Whisper APIz0No local STT available, using OpenAI Whisper APIz<No local STT available, using Mistral Voxtral Transcribe APIz.No local STT available, using xAI Grok STT APIz7No local STT available, using ElevenLabs Scribe STT API)rR   rL   DEFAULT_PROVIDER_HAS_FASTER_WHISPERrv   r   r{   r|   rF  _HAS_OPENAIr   rW   _HAS_MISTRALtools.xai_httprS  rM   )rO   explicitr   rS  s       r   _get_providerr]    s    *%% vZ'H~~j*:;;H  Ew" w!## '&$&& wNNK   6&&!## '&" WXXXwNNI   6v }^<< vNNI   6x  8::  xNNK   6y   !.? @ @ ! yNN;   6uCCCCCC++--11)<< uNNT   6|##122 $#|NNU   6  w  w }^44 DEEEv 022 FGGGx  &788 RSSSy??????''))--i88 	KKHIII5	    )** MNNN|6s   <=K< <
L	L	r8  r7  r8  r7  c                   |sdS |                                                                 }|t          v s|dk    rdS |t          t	          ||                    rdS 	 ddlm} ddlm}  |              ||          }| |d            ||          }n3# t          $ r&}	t                              d|	           Y d}	~	dS d}	~	ww xY w|dS 	 |                                }
n7# t          $ r*}	t                              d	||	d
           d}
Y d}	~	nd}	~	ww xY w|
s&t                              d|           ddd| d|dS t                              d|           	 |                    | ||          }nB# t          $ r5}	t                              d||	d
           ddd| d|	 |dcY d}	~	S d}	~	ww xY wt!          |t"                    sddd| d|dS |                    d|           |S )u  Route the call to a plugin-registered transcription provider, or
    return None.

    Returns the transcribe-response dict on dispatch, or ``None`` to
    fall through to the legacy "No STT provider available" error path.

    Resolution invariants enforced here:

    1. Built-in provider names short-circuit — never reach the plugin
       registry. The caller (``transcribe_audio``) handles ``local``,
       ``groq``, ``openai``, etc. via its existing elif chain; this
       function defensively rejects those names so a plugin can't be
       silently dispatched under a built-in name even if it somehow
       slipped past the registry's built-in shadow guard.
    2. Same-name command-type provider declared under
       ``stt.providers.<name>: type: command`` wins over a plugin. The
       caller short-circuits to the command runner before reaching us,
       but we re-verify here so a refactor of the caller can't silently
       break the invariant (matches TTS PR #17843 precedence rule).
    3. Plugin dispatch fires only when ``provider`` matches a
       registered :class:`TranscriptionProvider` whose ``name`` equals
       the configured value. Unknown names with no plugin registered
       return None (caller surfaces the legacy "No STT provider"
       message).
    4. Availability gating: when the matched plugin reports
       ``is_available() == False`` (missing API key, missing optional
       SDK, etc.) this returns an error envelope identifying the
       plugin as unavailable — **not** ``None`` — because the user
       explicitly opted into this plugin via ``stt.provider`` and the
       generic fallthrough message would be misleading.

    Provider exceptions are caught and converted into the standard
    error envelope (matches the legacy built-in error shapes — the
    gateway/CLI caller already expects ``{success: False, error:
    "...", transcript: ""}`` on failure).
    Nr   r   )get_provider)_ensure_plugins_discoveredT)forcez2STT plugin dispatch skipped (discovery failed): %suN   STT plugin provider '%s' is_available() raised: %s — treating as unavailableexc_infoFzRSTT plugin provider '%s' reports not available; returning unavailability envelope.rl   zSTT plugin 'uY   ' is not available — check that its required credentials / dependencies are configured.)r3  r4  r5  r   z-Transcribing with plugin STT provider '%s'...r^  z#STT plugin provider '%s' raised: %sz
' raised: z' returned a non-dict resultr   )r   rn   r   r   r   agent.transcription_registryr`  hermes_cli.pluginsra  rM   r{   r   is_availabler|   rF  
transcriber   r   
setdefault)r/  r   rO   r8  r7  r   r`  ra  plugin_providerr   	availablerJ  s               r   _dispatch_to_plugin_providerrl  i  s#   X  t
..


 
 
"
"C
###sf}}t "A&z377# # t======AAAAAA""$$$&,s++" '&T2222*l3//O   I3OOOttttt t#0022		   &'*C$ 	 	
 	
 	
 						  
'(+	
 	
 	

 Fs F F F 
 
 	
 KK?EEE
 ++ , 
 

  	
 	
 	
13d 	 	
 	
 	
 8C88388	
 
 	
 	
 	
 	
 	
 	
		
 fd## 
ECEEE	
 
 	
 j#&&&MsN   :B 
C	#CC	C& &
D0 DD!E: :
F9*F4.F94F9c           
      T   t          |           }t          j                            |          r	ddd|  dS |                                s	ddd|  dS |                                s	ddd|  dS |j                                        t          vr6ddd|j         dd		                    t          t                               dS 	 |                                j        }|t          k    rddd
|dz  ddt          dz  dddS n # t          $ r}ddd| dcY d}~S d}~ww xY wdS )z>Validate the audio file.  Returns an error dict or None if OK.Frl   zPath is a symbolic link: r3  r4  r5  r6  zPath is not a file: zUnsupported format: z. Supported: z, zFile too large: i   z.1fzMB (max z.0fzMB)zFailed to access file: N)r   r   pathislinkr[   is_filesuffixr   SUPPORTED_FORMATSr   sortedstatst_sizeMAX_FILE_SIZErG  )r/  
audio_path	file_sizees       r   _validate_audio_filer{    s   iJ	w~~j!! f =dYb=d=deee c =aV_=a=abbb a =_T]=_=_```  (999rJ,=rrDIIV\]nVoVoLpLprr
 
 	

	\OO%%-	}$$  uI,CuuuQ^bkQluuuu   %  \ \ \ =ZWX=Z=Z[[[[[[[[\ 4s   	=D 
D%D D% D%)	libcublaslibcudnn	libcudartzcannot be loadedzcannot open shared objectzno kernel image is availablezno CUDA-capable devicez#CUDA driver version is insufficientr   c                 b    t          |           t          fdt          D                       S )ag  Heuristic: is this exception a missing/broken CUDA runtime library?

    ctranslate2 raises plain RuntimeError with messages like
    ``Library libcublas.so.12 is not found or cannot be loaded``.  We want to
    catch missing/unloadable shared libs and driver-mismatch errors, NOT
    legitimate runtime failures ("CUDA out of memory", model bugs, etc.).
    c              3       K   | ]}|v V  	d S r   rV   )r   markermsgs     r   r   z-_looks_like_cuda_lib_error.<locals>.<genexpr>=  s'      CCv}CCCCCCrS   )r^   any_CUDA_LIB_ERROR_MARKERS)r   r  s    @r   _looks_like_cuda_lib_errorr  4  s4     c((CCCCC+BCCCCCCrS   c                     ddl m} 	  || dd          S # t          $ rC}t          |          s t                              d|            || dd          cY d}~S d}~ww xY w)	uc  Load faster-whisper with graceful CUDA → CPU fallback.

    faster-whisper's ``device="auto"`` picks CUDA when the ctranslate2 wheel
    ships CUDA shared libs, even on hosts where the NVIDIA runtime
    (``libcublas.so.12`` / ``libcudnn*``) isn't installed — common on WSL2
    without CUDA-on-WSL, headless servers, and CPU-only developer machines.
    On those hosts the load itself sometimes succeeds and the dlopen failure
    only surfaces at first ``transcribe()`` call.

    We try ``auto`` first (fast CUDA path when it works), and on any CUDA
    library load failure fall back to CPU + int8.
    r   WhisperModelautodevicecompute_typeu   faster-whisper CUDA load failed (%s) — falling back to CPU (int8). Install the NVIDIA CUDA runtime (libcublas/libcudnn) to use GPU.cpuint8N)r#   r  rM   r  r{   r|   )rw   r  r   s      r   _load_local_whisper_modelr  @  s     ,+++++
K|JvFKKKK K K K)#.. 	O	
 	
 	

 |Ju6JJJJJJJJJKs    
A#8AA#A#c                    t           st                      sddddS 	 t          t          |k    r,t                              d|           t          |          a|at                                          di                               d          pt          j
        t                    pd}d	d
i}|r||d<   	 t          j        | fi |\  }}d                    d |D                       }n# t          $ r}t          |          s t                              d|           dadaddlm}  ||dd          a|at          j        | fi |\  }}d                    d |D                       }Y d}~nd}~ww xY wt                              dt'          |           j        ||j        |j                   d|ddS # t          $ r0}	t                              d|	d           ddd|	 dcY d}	~	S d}	~	ww xY w)z.Transcribe using faster-whisper (local, free).Frl   zfaster-whisper not installedrn  NzELoading faster-whisper model '%s' (first load downloads the model)...r&   r7  	beam_sizer    c              3   H   K   | ]}|j                                         V  d S r   r  rn   r   segments     r   r   z$_transcribe_local.<locals>.<genexpr>v  0      !O!O7',"4"4"6"6!O!O!O!O!O!OrS   ul   faster-whisper CUDA runtime failed mid-transcribe (%s) — evicting cached model and retrying on CPU (int8).r   r  r  r  r  c              3   H   K   | ]}|j                                         V  d S r   r  r  s     r   r   z$_transcribe_local.<locals>.<genexpr>  r  rS   z;Transcribed %s via local whisper (%s, lang=%s, %.1fs audio)Tr@  zLocal transcription failed: %src  Local transcription failed: )rX  r   rF   rG   r{   rF  r  rN   rL   r   r   LOCAL_STT_LANGUAGE_ENVrh  r   rM   r  r|   r#   r  r   r   r7  durationr5  )
r/  rw   _forced_langtranscribe_kwargssegmentsrF  r4  r   r  rz  s
             r   _transcribe_localr  [  s     a$&& 	a$BA_```2a#4
#B#BKK_aklll4Z@@L * ""7B//33J?? y/00 	
 )!, 	9,8j)	P)4YTTBSTTNHd!O!Oh!O!O!OOOJJ 	P 	P 	P .c22 NND  
  L $333333'<
5vVVVL *)4YTTBSTTNHd!O!Oh!O!O!OOOJJJJJJ'	P* 	IOO *dmT]	
 	
 	

  zwOOO a a a5q4HHH =_\]=_=_````````asJ   BF< 74C, +F< ,
E96A9E4/F< 4E99AF< <
G6%G1+G61G6work_dirc                    t          |           }|j                                        t          v r| dfS t	                      }|sdS t
          j                            ||j         d          }|dd| |g}	 t          j
        |ddddt          j                   |dfS # t          j        $ r t                              d	|            Y d
S t          j        $ rn}|j                                        p'|j                                        pt'          |          }t                              d| |           dd| fcY d}~S d}~ww xY w)z.Normalize audio for local CLI STT when needed.N)NzOLocal STT fallback requires ffmpeg for non-WAV inputs, but ffmpeg was not foundr?   z-yz-iTr   checkcapture_outputr  r   r   z"ffmpeg conversion timed out for %s)Nz(Audio conversion for local STT timed outz#ffmpeg conversion failed for %s: %sz'Failed to convert audio for local STT: )r   rr  r   LOCAL_NATIVE_AUDIO_FORMATSrg   r   ro  r   stemr   r  r  r  r{   r5  r  r   rn   r   r^   )r/  r  rx  re   converted_pathr   rz  detailss           r   _prepare_local_audior    sr   iJ  $>>>$ ""F gffW\\(z,D,D,DEENtT9n=G	Iwd4dTW_i_qrrrrt##$ @ @ @99EEE???( I I I(..""@ahnn&6&6@#a&&:IwOOOHwHHHHHHHHHIs%   6'B *EEA#E<EEc           
         t                      }|sddt           ddS t                                          di                               d          pt	          j        t                    pt          }t          |          }	 t          j
        d          5 }t          | |          \  }}|rdd|dcd	d	d	           S |                    t          j        |          t          j        |          t          j        |          t          j        |          
          }t          t	          j        t          d                                                    }	|	r&t#          j        |dddddt"          j                   n6t#          j        t          j        |          ddddt"          j                   t+          t-          |                              d                    }
|
sddddcd	d	d	           S |
d                             d                                          }t2                              dt-          |           j        |t9          |                     d|ddcd	d	d	           S # 1 swxY w Y   d	S # t:          $ r}dddt           d| dcY d	}~S d	}~wt"          j        $ rp}|j                                        p'|j                                         ptC          |          }t2          "                    d| |           ddd| dcY d	}~S d	}~wtF          $ r0}t2          "                    d|d           ddd| dcY d	}~S d	}~ww xY w)zNRun the configured local STT command template and read back a .txt transcript.Frl   z5 not configured and no local whisper binary was foundrn  r&   r7  zhermes-local-stt-r:  N)r<  r=  r7  r8  Tr   )r  r  r  r  r   r   r  z*.txtzALocal STT command completed but did not produce a .txt transcriptr   r$  r%  z3Transcribed %s via local STT command (%s, %d chars)r   r@  zInvalid z  template, missing placeholder: z#Local STT command failed for %s: %szLocal STT failed: z7Unexpected error during local command transcription: %src  r  )$rt   rm   rN   rL   r   r   r  DEFAULT_LOCAL_STT_LANGUAGEr   rC  rD  r  r   ro   rp   r   rn   r   r  r  splitrt  r   globr(  r{   rF  r   r   KeyErrorr  r   r   r^   r5  rM   )r/  rw   r   r7  normalized_modelr=  prepared_input
prep_errorr   	use_shell	txt_filesrM  rz  r  s                 r   _transcribe_local_commandr    s   244 
(___	
 
 	
 	w++//
;; 	&9+,,	&% 
 6jAA1a(0CDDD "	a
)=i)T)T&NJ Q#(ZPP"	a "	a "	a "	a "	a "	a "	a "	a
 '-- ;~66 ;z22X..k"233	 .  G RY'<bAAGGIIJJI Hwd$tZ^hks}  tF  G  G  G  G  Gu{7334PT[_ilt~  uG  H  H  H  H tJ//44W==>>I $"$` +"	a "	a "	a "	a "	a "	a "	a "	a6 (l44g4FFLLNNOKKEY$ O$$	    $?P_``E"	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	a "	aH  
 
 
Z 5ZZWXZZ
 
 	
 	
 	
 	
 	
 	

 ( ] ] ](..""@ahnn&6&6@#a&&:IwOOO =[RY=[=[\\\\\\\\ a a aNPQ\`aaa =_\]=_=_````````as   J I84J D.I8/J <A/I8+J 8I<<J ?I< J 
MJ%M%M7A%L"M"M/%MMMc                 &   t          d          }|sddddS t          sddddS |t          v r(t                              d|t
                     t
          }	 dd	lm}m}m	}m
}  ||t          d
d          }	 t          | d          5 }|j        j                            ||d          }	ddd           n# 1 swxY w Y   t!          |	                                          }
t                              dt%          |           j        |t)          |
                     d|
ddt+          |dd          }t-          |          r |             S S # t+          |dd          }t-          |          r |             w w xY w# t.          $ r ddd|  dcY S |$ r}ddd| dcY d}~S d}~w|$ r}ddd| dcY d}~S d}~w|$ r}ddd| dcY d}~S d}~wt0          $ r0}t                              d|d           ddd| dcY d}~S d}~ww xY w)z8Transcribe using Groq Whisper API (free tier available).rP  Frl   zGROQ_API_KEY not setrn  openai package not installedz(Model %s not available on Groq, using %sr   OpenAIAPIErrorAPIConnectionErrorAPITimeoutError   rT  base_urlr   max_retriesrbr  r8  fileresponse_formatNz*Transcribed %s via Groq API (%s, %d chars)Tr   r@  closePermission denied: Connection error: Request timeout: API error: zGroq transcription failed: %src  Transcription failed: )r   rY  ry   r{   rF  DEFAULT_GROQ_STT_MODELr$   r  r  r  r  r2   openrH  transcriptionscreater^   rn   r   r   r   r  callablePermissionErrorrM   r5  )r/  rw   rT  r  r  r  r  client
audio_filetranscriptionrM  r  rz  s                r   _transcribe_groqr    sh   N++G U =STTT ] =[\\\ ]"">
Lbccc+
[PPPPPPPPPPPP-YZ[[[	i&& * & ; B B$#$* !C ! !               "-006688OKKDi-z3;O;OQ Q Q  $?PVWWFGT22E  FGT22E   ` ` ` =^S\=^=^_____ W W W =URS=U=UVVVVVVVV V V V =TQR=T=TUUUUUUUU P P P =N1=N=NOOOOOOOO [ [ [4a$GGG =YVW=Y=YZZZZZZZZ[s    F  8E #B7+E 7B;;E >B;?A%E $*F  -E==F   HHF'!H'H/F=7H=HGHH %HHHc                 T   	 t                      \  }}n*# t          $ r}ddt          |          dcY d}~S d}~ww xY wt          sddddS |t          v r(t
                              d|t                     t          }	 ddlm	}m
}m}m}  |||d	d
          }		 t          | d          5 }
|	j        j                            ||
|dk    rdnd          }ddd           n# 1 swxY w Y   t#          |          }t
                              dt%          |           j        |t)          |                     d|ddt+          |	dd          }t-          |          r |             S S # t+          |	dd          }t-          |          r |             w w xY w# t.          $ r ddd|  dcY S |$ r}ddd| dcY d}~S d}~w|$ r}ddd| dcY d}~S d}~w|$ r}ddd| dcY d}~S d}~wt0          $ r0}t
                              d|d           ddd| dcY d}~S d}~ww xY w)z+Transcribe using OpenAI Whisper API (paid).Frl   rn  Nr  z*Model %s not available on OpenAI, using %sr   r  r  r  r  r*   r  r   r  z,Transcribed %s via OpenAI API (%s, %d chars)Tr$   r@  r  r  r  r  r  zOpenAI transcription failed: %src  r  )rU   r   r^   rY  rz   r{   rF  DEFAULT_STT_MODELr$   r  r  r  r  r  rH  r  r  _extract_transcript_textr   r   r   r  r  r  rM   r5  )r/  rw   rT  r  r   r  r  r  r  r  r  r  rM  r  rz  s                  r   _transcribe_openair  1  s   
?AA 
 
 
XX
 
 	
 	
 	
 	
 	
 	

  ] =[\\\ [  @*N_```&
[PPPPPPPPPPPP(BTUVVV	i&& * & ; B B$#.8K.G.GFFV !C ! !               7}EEOKKFi-z3;O;OQ Q Q  $?PXYYFGT22E  FGT22E   ` ` ` =^S\=^=^_____ W W W =URS=U=UVVVVVVVV V V V =TQR=T=TUUUUUUUU P P P =N1=N=NOOOOOOOO [ [ [6DIII =YVW=Y=YZZZZZZZZ[s    
;6;;=F E' )+C E'  C$$E' 'C$(AE' ;*F '-FF H',H'0F>8H'>H'GH'H'G*$H'*H'7%H"H'"H'c           	         t          d          }|sddddS 	 	 ddlm}  |dd	           n# t          $ r Y nw xY wdd
lm}  ||          5 }t          | d          5 }|j        j        	                    ||t          |           j        d          }ddd           n# 1 swxY w Y   t          |          }t                              dt          |           j        |t          |                     d|ddcddd           S # 1 swxY w Y   dS # t           $ r ddd|  dcY S t"          $ rB}	t                              d|	d           dddt'          |	          j         dcY d}	~	S d}	~	ww xY w)zTranscribe using Mistral Voxtral Transcribe API.

    Uses the ``mistralai`` Python SDK to call ``/v1/audio/transcriptions``.
    Requires ``MISTRAL_API_KEY`` environment variable.
    rQ  Frl   zMISTRAL_API_KEY not setrn  r   r   zstt.mistralr   )Mistral)rT  r  )r-  	file_name)r8  r  Nz-Transcribed %s via Mistral API (%s, %d chars)Tr   r@  r  z Mistral transcription failed: %src  zMistral transcription failed: )r   r   r   r   mistralai.clientr  r  rH  r  completer   r   r  r{   rF  r   r  rM   r5  r   __name__)
r/  rw   rT  _lazy_ensurer  r  r  rJ  rM  rz  s
             r   _transcribe_mistralr  j  s    -..G X =VWWWr	>>>>>>Lu55555 	 	 	D	,,,,,,WW%%% 	[i&& *4==$%/d9oo>RSS >                 7v>>OKK?Y$j#o2F2F    $?PYZZ	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[  ` ` ` =^S\=^=^_____ r r r7TJJJ =p^bcd^e^e^n=p=pqqqqqqqqrs   . D 
;D ;D D!7B$D$B(	(D+B(	,AD?D DD DD E9.	E977E4.E94E9c           	      "   ddl m}  |            }t          |                    d          pd                                          }|sddddS t                      }|                    di           }t          |                    d	          p*t          d
          p|                    d	          pt                                                                        d          }t          |                    d          pt          j
        d          pt                                                    }t          |                    dd                    }	t          |                    dd                    }
	 ddl}ddl m} i }|r||d<   |	rd|d<   |
rd|d<   t          | d          5 }|                    | dd|  |            ddt#          |           j        |fi|d          }ddd           n# 1 swxY w Y   |j        dk    rd}	 |                                }|                    di                               dd          p|j        dd         }n# t,          $ r |j        dd         }Y nw xY wddd|j         d | dS |                                }|                    d!d                                          }|sddd"dS t.                              d#t#          |           j        |                    d|          |                    d$d          t3          |                     d|dd%S # t4          $ r ddd&|  dcY S t,          $ r0}t.                              d'|d(           ddd)| dcY d}~S d}~ww xY w)*zTranscribe using xAI Grok STT API.

    Uses the ``POST /v1/stt`` REST endpoint with multipart/form-data.
    Supports Inverse Text Normalization, diarization, and word-level timestamps.
    Requires ``XAI_API_KEY`` environment variable.
    r   rR  rT  rl   FzRNo xAI credentials found. Configure xAI OAuth in `hermes model` or set XAI_API_KEYrn  r   r  r4   /r7  r1   r   TdiarizeN)hermes_xai_user_agenttruer  z/sttzBearer )Authorizationz
User-Agentr  x   headersfilesdatar      r5  messager   zxAI STT API error (HTTP ): r  z!xAI STT returned empty transcriptz@Transcribed %s via xAI Grok STT (lang=%s, %.1fs audio, %d chars)r  r@  r  z xAI STT transcription failed: %src  zxAI STT transcription failed: )r[  rS  r^   rL   rn   rN   r   r4   rstripr   r   r  r   requestsr  r  postr   r   status_coder   r  rM   r{   rF  r   r  r5  )r/  rw   rS  credsrT  rO   
xai_configr  r7  
use_formatuse_diarizer  r  r  r  responserL  err_bodyrJ  rM  rz  s                        r   _transcribe_xair    s    <;;;;;((**E%))I&&,"--3355G 
i
 
 	
 "##Jr**Jz"" 	+,,	99Z  	 	 
 eggffSkk  z"" 	&9011	&%  egg	  !$!?!?@@J!*..E"B"BCCK?c888888! 	('D 	$#DN 	%$DO)T"" 	j}}!!!%8w%8%8"7"7"9"9 
 T)__1:>  %  H	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 3&&F-#==??!gr2266y"EE\W[X[W[I\ - - -!tt,- ! UH4HUUVUU    **VR006688 	  <   	NOO JJz8,,JJz1%%  	
 	
 	
  ERRR ` ` ` =^S\=^=^_____ c c c7TJJJ =a^_=a=abbbbbbbbcs|   ;1M  ,AG:.M  :G>>M  G>M  AI!  M  !I=:M  <I==M  AM  A*M   N	N%N	N	Nc           	      D   t          d          }|sddddS t                      }|                    di           }t          |                    d          pt          d          pt                                                                        d	          }t          |                    d
          pd                                          }t          |                    dd                    }t          |                    dd                    }	 ddl}	||rdnd|rdndd}
|r||
d
<   t          | d          5 }|	
                    | dd|idt          |           j        |fi|
d          }ddd           n# 1 swxY w Y   |j        dk    rd}	 |                                }|                    d          p|                    d          }t          |t                     r%t          |                    d          p|          }n!|rt          |          }n|j        dd         }n# t$          $ r |j        dd         }Y nw xY wddd|j         d| dS |                                }t'          |          }|sddddS t(                              d t          |           j        |t-          |                     d!|dd"S # t.          $ r ddd#|  dcY S t$          $ r0}t(                              d$|d!%           ddd&| dcY d}~S d}~ww xY w)'z+Transcribe using ElevenLabs Scribe STT API.rV  Frl   zELEVENLABS_API_KEY not setrn  rU  r  r5   r  language_codetag_audio_eventsr  r   Nr  false)model_idr  r  r  z/speech-to-textz
xi-api-keyr  r  r  r  rL  r5  r  r   zElevenLabs STT API error (HTTP r  z(ElevenLabs STT returned empty transcriptz3Transcribed %s via ElevenLabs Scribe (%s, %d chars)Tr@  r  z'ElevenLabs STT transcription failed: %src  z%ElevenLabs STT transcription failed: )r   rN   rL   r^   r5   rn   r  r   r  r  r  r   r   r  r   r   r   r  rM   r  r{   rF  r   r  r5  )r/  rw   rT  rO   elevenlabs_configr  r  r  r  r  r  r  r  rL  r  error_valuerJ  rM  rz  s                      r   _transcribe_elevenlabsr     s   011G [ =YZZZ!##J"|R88j)) 	#233	#"  eggffSkk	 
 )--o>>D"EEKKMMM&'8'<'<=OQV'W'WXX/33IuEEFFG=j #*: G!(5vvg 
  

  	2$1D!)T"" 	j}},,,%w/Y 4jAB %  H	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 3&&F
-#==??&ll844MW8M8Mk400 1 !;!;!J{KKFF  1 --FF%]4C40F - - -!tt,- ! \8;O\\TZ\\   26:: 	  C   	AOO   		
 	
 	
  LYYY ` ` ` =^S\=^=^_____ j j j>DQQQ =hef=h=hiiiiiiiijsz   )K ?6F5K FK F	K BH4 3K 4IK IK $*K AK L&	L/%LLLc                 ~   t          |           }|r|S t                      }t          |          sddddS t          |          }|dk    rP|                    di           }t          |p|                    dt                              }t          | |          S |dk    rP|                    di           }t          |p|                    dt                              }t          | |          S |dk    r|pt          }t          | |          S |d	k    rC|                    d	i           }|p|                    dt                    }t          | |          S |d
k    rC|                    d
i           }|p|                    dt                    }t          | |          S |dk    r|pd}t!          | |          S |dk    rC|                    di           }	|p|	                    dt"                    }t%          | |          S t'          ||          }
|
t)          | ||
||          S t+          |                    |          t,                    r|                    |i           ni }|                    d          }|p|                    d          }t/          | ||||          }||S dddt0           ddS )a  
    Transcribe an audio file using the configured STT provider.

    Provider priority:
      1. User config (``stt.provider`` in config.yaml)
      2. Auto-detect: local > Groq > OpenAI > Mistral > xAI > ElevenLabs

    Args:
        file_path: Absolute path to the audio file to transcribe.
        model:     Override the model. If None, uses config or provider default.

    Returns:
        dict with keys:
          - "success" (bool): Whether transcription succeeded
          - "transcript" (str): The transcribed text (empty on failure)
          - "error" (str, optional): Error message if success is False
          - "provider" (str, optional): Which provider was used
    Frl   z4STT is disabled in config.yaml (stt.enabled: false).rn  r&   r8  r   r   r$   r   r   zgrok-sttrU  r  N)r1  r7  r^  zZNo STT provider available. Install faster-whisper for free local transcription, configure a/   or install a local whisper CLI, set GROQ_API_KEY for free Groq Whisper, set MISTRAL_API_KEY for Mistral Voxtral Transcribe, configure xAI OAuth or set XAI_API_KEY for xAI Grok STT, set ELEVENLABS_API_KEY for ElevenLabs Scribe, or set VOICE_TOOLS_OPENAI_KEY or OPENAI_API_KEY for the OpenAI Whisper API.)r{  rN   rR   r]  rL   r   r}   r  r   r  r  r  r  r  DEFAULT_MISTRAL_STT_MODELr  r  DEFAULT_ELEVENLABS_STT_MODELr   r   rN  r   r   rl  rm   )r/  r8  r5  rO   r   	local_cfgrw   
openai_cfgmistral_cfgelevenlabs_cfgcommand_provider_config
plugin_cfgplugin_languageplugin_modelplugin_results                  r   transcribe_audior  W  s,   ( !++E  "##J*%% 
K
 
 	
 Z((H7NN7B//	+@Y]]7,?@@
 

 !J777?""NN7B//	3@Y]]7,?@@
 

 )J???644
	:6668^^Hb11
HjnnW6GHH
!)Z8889 nnY33Qkoog7PQQ
"9j9995(j
y*555<#b99Zn00=YZZ
%i<<< C8ZXX*&# 
 
 
 	
* 2<JNN8<T<TVZ1[1[c"---acJ nnZ00O3JNN733L0   M   <(=< < <	  rS   c                     t                      } |                     di           }|                    dd          }|                    dd          }|r||pt          fS t                      }|r	|t          fS t	          d          }|4d}t                      r|dt          d	          z   z  }t          |          |j        t          |j
                            d
           d
d          fS )z@Return direct OpenAI audio config or a managed gateway fallback.r$   rT  rl   r  zopenai-audioNzUNeither stt.openai.api_key in config nor VOICE_TOOLS_OPENAI_KEY/OPENAI_API_KEY is setz. z&managed OpenAI audio for transcriptionr  v1)rN   rL   OPENAI_BASE_URLr   r	   r
   r   r   nous_user_tokenr   gateway_originr  )rO   r  cfg_api_keycfg_base_urldirect_api_keymanaged_gatewayr  s          r   rU   rU     s   !##J"--J..B//K>>*b11L >\<_==133N /..2>BBOi%'' 	7< G !!!*G)0055888$- -  rS   r  c                    t          | t                    r|                                 S t          | d          r9t	          | d          }t          |t                    r|                                S t          | t
                    r>|                     d          }t          |t                    r|                                S t          |                                           S )zBNormalize text and JSON transcription responses to a plain string.r  )r   r^   rn   hasattrr  r   rL   )r  r   s     r   r  r    s    -%% %""$$$}f%% !v..eS!! 	!;;== -&& !!!&))eS!! 	!;;== }##%%%rS   r   )t__doc__loggingr   ro   r_   r   rC  pathlibr   typingr   r   r   urllib.parser   utilsr   tools.managed_tool_gatewayr	   tools.tool_backend_helpersr
   r   r   	getLoggerr  r{   r   r   r   r   r^   r   r"   rX  rY  rZ  rW  r}   r  r   r  r  r  r  rm   r  rZ   r2   r  r4   r5   rs  r  rw  ry   rz   rF   object__annotations__rG   r   rN   rR   rW   rc   rg   rj   rt   rv   r   r   r   	frozensetr   r   rB  r   r   r   r   r   r   r   r   r   r   r   intr   r   r   r  r  r  r!  r.  rN  r]  rl  r{  r  BaseExceptionr  r  r  tupler  r  r  r  r  r  r   r  rU   r  rV   rS   r   <module>r(     sQ    8  				              & & & & & & & & & &             ! ! ! ! ! ! C C C C C C          
	8	$	$/ / / /$      I I I I I I &o&677 oh''{++   ! BI0+>> "#35MNN %BI&9;PQQ (ry)?MM 2 4 ? 	/+KLL")13NOO29/1FGG #")$=?]^^ hhh 666   MLLZZZ "&hv % % %#' 8C= ' ' '$    2 2x~ 2 2 2 2 24    %c %hsm % % % %"Xc] " " " "#hsm # # # #Xc]    5D 5 5 5 5x}     *.x} . . . . .t    < "	 # # #   < '* ## $) !&Y'D'D'DEE 8c3h 8s 8tCH~ 8 8 8 8S#X
 
#s(^   <>DcN >t > > > >S#X d38n   * DcN         htCH~.F RV    	T#s(^ 	 	 	 	 	[4S> [c [ [ [ [s c hsm    @# hsm PS    (%%sCx.% 	% % % %P>j.> >4 > > > >B(Qc (QE (Qj6Q (Q (Q (Q (QV$  # #    F %)~ ~~~ cN~ S#X	~
 SM~ 
#s(^~ ~ ~ ~Bxd xs x x x xF ,0L
  "L L LLL c3h(L
 C=L smL d38nL L L LhC HT#s(^,D    V	 	DM 	Dd 	D 	D 	D 	DK# K K K K6:a :a# :a$sCx. :a :a :a :azIC I3 I5#PXY\P]A];^ I I I I2Ea Ea# Ea$sCx. Ea Ea Ea EaX-[ -[ -[c3h -[ -[ -[ -[h2[# 2[3 2[4S> 2[ 2[ 2[ 2[r$r3 $rC $rDcN $r $r $r $rXccs cc ccS#X cc cc cc ccVNjc Njs NjtCH~ Nj Nj Nj Njl  HSM T#s(^    DU38_    <&C &C & & & & & &rS   