
    ,Vj              	          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  ej        e          ZdJd	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)dZ*dZ+dZ, ej&        dd          Z- ej&        dd          Z. ej&        d d!          Z/h d"Z0h d#Z1d$Z2h d%Z3h d&Z4da5e
e6         e7d'<   da8e
e         e7d(<   de9fd)Z:dJd*e
e9         defd+Z;defd,Z<d-ede
e         fd.Z=de
e         fd/Z>de
e         fd0Z?de
e         fd1Z@defd2ZAd3e
e         defd4ZBd3e
e         defd5ZCd*e9defd6ZDd7ede
eeef                  fd8ZEd9ZFd:eGdefd;ZHd3efd<ZId7ed3edeeef         fd=ZJd7ed>edeKe
e         e
e         f         fd?ZLd7ed3edeeef         fd@ZMd7ed3edeeef         fdAZNd7ed3edeeef         fdBZOd7ed3edeeef         fdCZPd7ed3edeeef         fdDZQdJd7edEe
e         deeef         fdFZRdeKeef         fdGZSdHedefdIZTdS )Ku  
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.

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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       7/root/.hermes/hermes-agent/tools/transcription_tools.pyr   r   -   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!   B   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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>
   .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   rF   get	ExceptionrE   s    r   _load_stt_configrJ   q   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   )rJ   rH   r   )rK   rM   s     r   is_stt_enabledrN   z   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    rO   r   _has_openai_audio_backendrS      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)rT   	directory	candidates      r   _find_binaryr_      sm    * " "	OOk1	 	"")Irw"?"? 	"y>>!!!<$$$rO   c                       t          d          S )Nffmpegr_   rR   rO   r   _find_ffmpeg_binaryrc      s    !!!rO   c                       t          d          S )Nwhisperrb   rR   rO   r   _find_whisper_binaryrf      s    	"""rO   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striprf   shlexquote)
configuredwhisper_binaryquoted_binarys      r   _get_local_command_templaterp      sn    0"55;;==J )++N 
N33 > > >	
 4rO   c                  "    t                      d uS r   )rp   rR   rO   r   _has_local_commandrr      s    &((44rO   
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rs   s    r   _normalize_local_modelr{      sn      	#}44
k8Q8Q 	:66*:S:SNNM #   #"rO   c                      t          |           S r   )r{   rz   s    r   _normalize_local_command_modelr}      s    !*---rO   c                    t          |           sdS d| v }|                     dt                    }|rr|dk    r5t          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    r-t          d          rdS t
                              d           dS |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 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).
    noneproviderr%   local_commandzhSTT 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 unavailablegroqGROQ_API_KEYz7STT provider 'groq' configured but GROQ_API_KEY not setr#   z9STT provider 'openai' configured but no API key availablemistralMISTRAL_API_KEYz`STT provider 'mistral' configured but mistralai package not installed or MISTRAL_API_KEY not setxaiXAI_API_KEYz5STT provider 'xai' configured but XAI_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 API)rN   rH   DEFAULT_PROVIDER_HAS_FASTER_WHISPERrr   rw   rx   info_HAS_OPENAIr   rS   _HAS_MISTRAL)rK   explicitr   s      r   _get_providerr      s    *%% vZ'H~~j*:;;H  8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]++ uNNG   6  w  }^44 DEEEv 022 FGGGx &788 RSSSy]## DEEEu6rO   	file_pathc           
         t          |           }|                                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.Frh   zAudio file not found: success
transcripterrorz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   rW   is_filesuffixlowerSUPPORTED_FORMATSjoinsortedstatst_sizeMAX_FILE_SIZEOSError)r   
audio_path	file_sizees       r   _validate_audio_filer   *  s   iJ 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   !=C   
C=*C82C=8C=)	libcublaslibcudnn	libcudartzcannot be loadedzcannot open shared objectzno kernel image is availablezno CUDA-capable devicez#CUDA driver version is insufficientexcc                 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   rR   ).0markermsgs     r   	<genexpr>z-_looks_like_cuda_lib_error.<locals>.<genexpr>h  s'      CCv}CCCCCCrO   )rZ   any_CUDA_LIB_ERROR_MARKERS)r   r   s    @r   _looks_like_cuda_lib_errorr   _  s4     c((CCCCC+BCCCCCCrO   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   rI   r   rw   rx   )rs   r   r   s      r   _load_local_whisper_modelr   k  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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).Frh   zfaster-whisper not installedr   NzELoading faster-whisper model '%s' (first load downloads the model)...r%   language	beam_size    c              3   H   K   | ]}|j                                         V  d S r   textrj   r   segments     r   r   z$_transcribe_local.<locals>.<genexpr>  0      !O!O7',"4"4"6"6!O!O!O!O!O!OrO   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   rO   z;Transcribed %s via local whisper (%s, lang=%s, %.1fs audio)Tr   r   r   zLocal transcription failed: %sexc_infoLocal transcription failed: )r   rB   rC   rw   r   r   rJ   rH   r   r   LOCAL_STT_LANGUAGE_ENV
transcriber   rI   r   rx   r"   r   r   r   r   durationr   )
r   rs   _forced_langtranscribe_kwargssegmentsr   r   r   r   r   s
             r   _transcribe_localr     s     ] =[\\\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. )4C F. 
E+(A9E&!F. &E++AF. .
G(8%G#G(#G(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f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-iT)checkcapture_outputr   z#ffmpeg conversion failed for %s: %sz'Failed to convert audio for local STT: )r   r   r   LOCAL_NATIVE_AUDIO_FORMATSrc   r   pathr   stem
subprocessrunCalledProcessErrorstderrrj   stdoutrZ   rw   r   )r   r   r   ra   converted_pathcommandr   detailss           r   _prepare_local_audior     s3   iJ  $>>>$ ""F gffW\\(z,D,D,DEENtT9n=GIwd4dKKKKt##( 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 D!A#D
D
D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          j        |dddd           t#          t%          |                              d                    }	|	sddddcd	d	d	           S |	d                             d                                          }
t,                              dt%          |           j        |t3          |
                     d|
ddcd	d	d	           S # 1 swxY w Y   d	S # t4          $ r}dddt           d| dcY d	}~S d	}~wt          j        $ rp}|j                                        p'|j                                        pt=          |          }t,                              d| |           ddd| dcY d	}~S d	}~wt@          $ r0}t,                              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.Frh   z5 not configured and no local whisper binary was foundr   r%   r   zhermes-local-stt-)prefixN)
input_path
output_dirr   modelT)shellr   r   r   z*.txtzALocal STT command completed but did not produce a .txt transcriptr   zutf-8)encodingz3Transcribed %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: %sr   r   )!rp   ri   rJ   rH   r   r   r   DEFAULT_LOCAL_STT_LANGUAGEr}   tempfileTemporaryDirectoryr   formatrk   rl   r   r   r   r   glob	read_textrj   rw   r   r   lenKeyErrorr   r   r   rZ   r   rI   )r   rs   command_templater   normalized_modelr   prepared_input
prep_errorr   	txt_filestranscript_textr   r   s                r   _transcribe_local_commandr     s   244 
(___	
 
 	
 	w++//
;; 	&9+,,	&% 
 6jAA+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 N7$d4VZ[[[[tJ//44W==>>I $"$` 	a 	a 	a 	a 	a 	a 	a 	a* (l44g4FFLLNNOKKEY$ O$$	    $?P_``9	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a 	a<  
 
 
Z 5ZZWXZZ
 
 	
 	
 	
 	
 	
 	

 ( ] ] ](..""@ahnn&6&6@#a&&:IwOOO =[RY=[=[\\\\\\\\ a a aNPQ\`aaa =_\]=_=_````````as   H G:4H B0G:1H >A/G:-H :G>>H G>H 
K!H'!K!'K!9A%J$K!$K!1%KK!K!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).r   Frh   zGROQ_API_KEY not setr   openai package not installedz(Model %s not available on Groq, using %sr   OpenAIAPIErrorAPIConnectionErrorAPITimeoutError   api_keybase_urltimeoutmax_retriesrbr   r   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: %sr   Transcription failed: )r   r   ru   rw   r   DEFAULT_GROQ_STT_MODELr#   r   r   r   r   r/   openaudiotranscriptionscreaterZ   rj   r   r   r   getattrcallablePermissionErrorrI   r   )r   rs   r   r   r   r   r   client
audio_filetranscriptionr   r  r   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).Frh   r   Nr   z*Model %s not available on OpenAI, using %sr   r   r   r   r  r)   r   jsonr  z,Transcribed %s via OpenAI API (%s, %d chars)Tr#   r   r  r	  r
  r  r  zOpenAI transcription failed: %sr   r  )rQ   r   rZ   r   rv   rw   r   DEFAULT_STT_MODELr#   r   r   r   r   r  r  r  r  _extract_transcript_textr   r   r   r  r  r  rI   r   )r   rs   r   r  r   r   r   r   r   r  r  r  r   r  r   s                  r   _transcribe_openair  R  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}  ||          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.
    r   Frh   zMISTRAL_API_KEY not setr   r   )Mistral)r   r  )content	file_name)r   r  Nz-Transcribed %s via Mistral API (%s, %d chars)Tr   r   r	  z Mistral transcription failed: %sr   zMistral transcription failed: )r   mistralai.clientr   r  r  r  completer   r   r  rw   r   r   r  rI   r   type__name__)	r   rs   r   r   r  r  resultr   r   s	            r   _transcribe_mistralr(    s[    -..G X =VWWWr,,,,,,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sj   C4 C'7A?3C'?B	C'B	AC'C4 'C++C4 .C+/C4 4E		E7E	EEc           	         t          d          }|sddddS t                      }|                    di           }t          |                    d          pt          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          t1          |                     d|dd$S # t2          $ 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   Frh   zXAI_API_KEY not setr   r   r  r1   /r   r.   r   Tdiarizer   N)hermes_xai_user_agenttruer  z/sttzBearer )Authorizationz
User-Agentr  x   )headersfilesdatar     r   messagei,  zxAI STT API error (HTTP z): 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: %sr   zxAI STT transcription failed: )r   rJ   rH   rZ   r1   rj   rstripr   r   r   r   requeststools.xai_httpr,  r  postr   r   status_coder  r   rI   rw   r   r   r  r   )r   rs   r   rK   
xai_configr  r   
use_formatuse_diarizer6  r,  r2  r  responsedetailerr_bodyr'  r   r   s                      r   _transcribe_xair@    sE    M**G T =RSSS!##Jr**Jz"" 	+,,	  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|   /1K4  AF."K4 .F22K4 5F26K4 AH K4 H1.K4 0H11K4 AK4 	A*K4 4M		M%L=7M=Mr   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ddt"           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 faster-whisper (free) > Groq (free tier) > OpenAI (paid)

    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
    Frh   z4STT is disabled in config.yaml (stt.enabled: false).r   r%   r   r   r   r#   r   r   zgrok-sttzZNo STT provider available. Install faster-whisper for free local transcription, configure z or install a local whisper CLI, set GROQ_API_KEY for free Groq Whisper, set MISTRAL_API_KEY for Mistral Voxtral Transcribe, set XAI_API_KEY for xAI Grok STT, or set VOICE_TOOLS_OPENAI_KEY or OPENAI_API_KEY for the OpenAI Whisper API.)r   rJ   rN   r   rH   r{   ry   r   r}   r   r  r  r  r  DEFAULT_MISTRAL_STT_MODELr(  r@  ri   )	r   r   r   rK   r   	local_cfgrs   
openai_cfgmistral_cfgs	            r   transcribe_audiorF    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 <(=< < <	
 
 
rO   c                     t                      } |                     di           }|                    dd          }|                    dd          }|r||pt          fS t                      }|r	|t          fS t	          d          }|$d}t                      r|dz  }t          |          |j        t          |j	        
                    d	           d	d
          fS )z@Return direct OpenAI audio config or a managed gateway fallback.r#   r   rh   r  zopenai-audioNzUNeither stt.openai.api_key in config nor VOICE_TOOLS_OPENAI_KEY/OPENAI_API_KEY is setz5, and the managed OpenAI audio gateway is unavailabler*  v1)rJ   rH   OPENAI_BASE_URLr   r	   r
   r   nous_user_tokenr   gateway_originr5  )rK   rD  cfg_api_keycfg_base_urldirect_api_keymanaged_gatewayr4  s          r   rQ   rQ   g  s    !##J"--J..B//K>>*b11L >\<_==133N /..2>BBOi%'' 	ONNG!!!*G)0055888$- -  rO   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   )
isinstancerZ   rj   hasattrr  dictrH   )r  r   s     r   r  r    s    -%% %""$$$}f%% !v..eS!! 	!;;== -&& !!!&))eS!! 	!;;== }##%%%rO   r   )U__doc__loggingr   rk   r[   r   r   pathlibr   typingr   r   r   urllib.parser   utilsr   tools.managed_tool_gatewayr	   tools.tool_backend_helpersr
   r   	getLoggerr&  rw   r   importlib.utilutilr   rZ   boolr!   r   r   r   r   ry   r   r   r  r  rB  ri   r   rV   r/   rI  r1   r   r   r   ru   rv   rB   object__annotations__rC   rS  rJ   rN   rS   r_   rc   rf   rp   rr   r{   r}   r   r   r   BaseExceptionr   r   r   tupler   r   r  r  r(  r@  rF  rQ   r  rR   rO   r   <module>rd     s    6  				              & & & & & & & & & &             ! ! ! ! ! ! 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 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} . . . . .[d [s [ [ [ [DC HT#s(^,D    R	 	DM 	Dd 	D 	D 	D 	DK# K K K K69a 9a# 9a$sCx. 9a 9a 9a 9axIC I3 I5#PXY\P]A];^ I I I I,?a ?a# ?a$sCx. ?a ?a ?a ?aL-[ -[ -[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N[cs [c [cS#X [c [c [c [cFO O OHSM OT#s(^ O O O OdU38_    2&C &C & & & & & &rO   