
    љi1                     ,   S SK JrJr  S SKJrJr  S SKrS SKrS SKrS SK	J
r
JrJrJrJrJr  S SKrS SKr S SKJrJr  \R,                  " \5      r\ " S S5      5       r\ " S S	5      5       r " S
 S\5      r " S S\5      r " S S5      rg!    NU= f)    )ABCabstractmethod)	dataclassasdictN)DictOptionalAnyTupleIterableList)VoiceEncoderpreprocess_wavc                   h    \ rS rSr% Sr\\S'   \\S'   \\\	4   \S'   Sr
\\S'   S\\\	4   4S	 jrS
rg)	Candidate   z*A single candidate item from Top-K search.
speaker_id
similaritymetadataFis_newreturnc                     [        U 5      $ N)r   selfs    `/home/james-whalen/.local/lib/python3.13/site-packages/aiavatar/sts/stt/speaker_registry/base.pyto_dictCandidate.to_dict   s    d|     N)__name__
__module____qualname____firstlineno____doc__str__annotations__floatr   r	   r   boolr   __static_attributes__r   r   r   r   r      s<    4O38nFDc3h r   r   c                   L    \ rS rSr% Sr\\S'   \\   \S'   S\\	\
4   4S jrSrg)	MatchTopKResult   a  
Top-K matching outcome with decision folded into chosen.is_new.
- chosen: Top-1 if matched; or newly registered id (similarity=1.0, empty metadata) if not matched.
          chosen.is_new == True iff a new speaker was registered.
- candidates: nearest existing neighbors for reference.
    * When chosen.is_new == False: excludes chosen (starts from rank-2).
    * When chosen.is_new == True: includes the original Top-1 as candidates[0].
chosen
candidatesr   c                     U R                   R                  5       U R                   Vs/ s H  oR                  5       PM     snS.$ s  snf )Nr-   r.   )r-   r   r.   )r   cs     r   r   MatchTopKResult.to_dict*   s9    kk))+04@199;@
 	
@s   Ar   N)r    r!   r"   r#   r$   r   r&   r   r   r%   r	   r   r)   r   r   r   r+   r+      s-     Y
c3h 
r   r+   c                      \ rS rSrSr\SS\S\R                  S\	\
\\4      SS4S jj5       r\S\S\\R                  \
\\4   4   4S	 j5       r\S\S
\S\SS4S j5       r\SS\S
\S\S\4S jj5       r\S\\\\R                  \
\\4   4      4S j5       r\S\4S j5       r\S\R                  S\S\\\\4      4S j5       rS\SS4S jrS\SS4S jrSrg)BaseSpeakerStore1   z5Abstract storage for speaker embeddings and metadata.Nr   	embeddingr   r   c                     g)z=Insert or update an L2-normalized embedding and its metadata.Nr   )r   r   r6   r   s       r   upsertBaseSpeakerStore.upsert4   s     	r   c                     g r   r   )r   r   s     r   getBaseSpeakerStore.get9       r   keyvaluec                     g r   r   r   r   r>   r?   s       r   set_metadataBaseSpeakerStore.set_metadata=   r=   r   defaultc                     g r   r   r   r   r>   rD   s       r   get_metadataBaseSpeakerStore.get_metadataA   r=   r   c                     g r   r   r   s    r   	all_itemsBaseSpeakerStore.all_itemsE   r=   r   c                     g r   r   r   s    r   countBaseSpeakerStore.countI   r=   r   q_normkc                     g)z
Return top-k (speaker_id, cosine_similarity) against normalized query q_norm.
All stored embeddings must be L2-normalized (cosine = dot).
Nr   )r   rO   rP   s      r   topk_similarity BaseSpeakerStore.topk_similarityM   s     	r   pathc                     [         er   NotImplementedErrorr   rT   s     r   save_to_fileBaseSpeakerStore.save_to_fileV       !!r   c                     [         er   rV   rX   s     r   load_from_fileBaseSpeakerStore.load_from_fileY   r[   r   r   r   )r    r!   r"   r#   r$   r   r%   npndarrayr   r   r	   r8   r
   r;   rB   rG   r   rJ   intrM   r   r'   rR   rY   r]   r)   r   r   r   r4   r4   1   s   ?  xPTUXZ]U]P^G_ ko   c eBJJS#X,F&G   s  S T   s  s c   8E#rzz4S>*I$JK   s   bjj S T%U
BS=T  " " ""3 "4 "r   r4   c            
          \ rS rSrSrSS\\   4S jjrSS\S\R                  S\\
\\4      S	S4S
 jjrS\S	\\R                  \
\\4   4   4S jrS\S\S\S	S4S jrSS\S\S\S	\4S jjrS	\\\\R                  \
\\4   4      4S jrS	\4S jrSS jrS\R                  S\S	\\\\4      4S jrSS jrSS jrSrg)InMemoryStore]   z
In-memory store with BLAS-backed top-k via a dense (N, D) matrix.
Provides optional file persistence ({base}.npz for ids+embeddings, {base}.json for metadata).
N	data_pathc                 Z    0 U l         / U l        S U l        Xl        U R	                  5         g r   )_store_id_list_emb_matrixre   r]   )r   re   s     r   __init__InMemoryStore.__init__b   s)    13#%15"r   r   r6   r   r   c                    XR                   ;  nU(       aS  UR                  [        R                  SS9U=(       d    0 S.U R                   U'   U R                  R                  U5        OVUR                  [        R                  SS9U R                   U   S'   U(       a!  U R                   U   S   R                  U5        S U l        U R                  5         g )NFcopyr6   r   r6   r   )	rg   astyper_   float32rh   appendupdateri   rY   )r   r   r6   r   r   s        r   r8   InMemoryStore.upsertk   s    ;;.4=4D4DRZZV[4D4\4<N'EDKK
#MM  ,3<3C3CBJJUZ3C3[DKK
#K0J'
3::8Dr   c                 n    U R                   R                  U5      nUc  [        SU 35      eUS   US   4$ )NUnknown speaker_id: r6   r   rg   r;   KeyError)r   r   slots      r   r;   InMemoryStore.getx   sA    {{z*<1*>??K $z"222r   r>   r?   c                     U R                   R                  U5      nUc  [        SU 35      eX4S   U'   U R                  5         g Nrv   r   )rg   r;   rx   rY   )r   r   r>   r?   ry   s        r   rB   InMemoryStore.set_metadata~   sE    {{z*<1*>?? %Zr   rD   c                     U R                   R                  U5      nUc  [        SU 35      eUS   R                  X#5      $ r|   rw   )r   r   r>   rD   ry   s        r   rG   InMemoryStore.get_metadata   sB    {{z*<1*>??J##C11r   c              #   j   #    U R                   R                  5        H  u  pXS   US   4v   M     g 7f)Nr6   r   )rg   items)r   sidpayloads      r   rJ   InMemoryStore.all_items   s3      KK--/LC{+WZ-@@@ 0s   13c                 ,    [        U R                  5      $ r   )lenrg   r   s    r   rM   InMemoryStore.count   s    4;;r   c                    U R                   b  gU R                  (       d)  [        R                  " S[        R                  S9U l         g/ n/ nU R
                   HH  nU R                  R                  U5      nUc  M#  UR                  U5        UR                  US   5        MJ     U R                  R                  5        H1  u  p4X1;  d  M  UR                  U5        UR                  US   5        M3     Xl        U(       a8  [        R                  " U5      R                  [        R                  SS9U l         g[        R                  " S[        R                  5      U l         g)z*Rebuild (N, D) matrix and aligned id list.Nr   r   dtyper6   Frm   )ri   rg   r_   zerosrq   rh   r;   rr   r   vstackrp   )r   new_idsrowsr   r   s        r   _ensure_matrixInMemoryStore._ensure_matrix   s	   '{{!xxbjjAD!#==Ckkooc*GNN3KK,- ! !KK--/LC!s#GK01 0
  MQ299T?11"**51IWYW_W_`fhjhrhrWsr   rO   rP   c                    U R                  5         U R                  R                  S:X  a  [        S5      eU R                  U-  n[	        S[        X#R                  S   5      5      n[        R                  " X2* 5      U* S nU[        R                  " X4   5      SSS2      nU Vs/ s H  oPR                  U   [        X5   5      4PM      sn$ s  snf )z5Compute top-k using a single matvec and argpartition.r   zInMemoryStore is empty.   N)r   ri   sizeRuntimeErrormaxminshaper_   argpartitionargsortrh   r'   )r   rO   rP   simsidxis         r   rR   InMemoryStore.topk_similarity   s      A%899&(3q**Q-()oodB',"**TY'"-.<?@Cqq!5>2C@@@s   '%Cc           	      "   U R                   (       d  gSU l        U R                  5         U R                   Vs0 s H  oU R                  U   S   _M     nnU R                  b  U R                  R
                  S:X  a\  [        R                  " U R                    S3[        R                  " / [        S9[        R                  " S[        R                  S9S9  Oj[        R                  " U R                    S3[        R                  " U R                  [        S9U R                  R                  [        R                  SS	9S9  [        U R                    S
3SSS9 n[        R                  " X#SSS9  SSS5        gs  snf ! , (       d  f       g= f)z?Write {path}.npz (ids + embeddings) and {path}.json (metadata).Nr   r   .npzr   r   )ids
embeddingsFrm   .jsonwutf-8encoding   )ensure_asciiindent)re   ri   r   rh   rg   r   r_   savez_compressedarrayobjectr   rq   rp   openjsondump)r   r   metasfs       r   rY   InMemoryStore.save_to_file   s9   ~~>BmmLmsdkk#&z22mL#t'7'7'<'<'A4>>"2$ 7$&HHRv$>+-88F"**+MO 4>>"2$ 7$&HHT]]&$I+/+;+;+B+B2::TY+B+Z\ T^^$E*C'BaIIeU1= CB M CBs   E;F  
Fc                 N   U R                   (       d  gU R                    S3nU R                    S3n[        R                  R                  U5      (       a$  [        R                  R                  U5      (       dJ  U R                  R                  5         / U l        [        R                  " S[        R                  S9U l
        g[        R                  " USS9nUS   R                  5       nUS	   n[        US
SS9 n[        R                  " U5      nSSS5        U R                  R                  5         / U l        [        U5       Hh  u  p[        R                   " XX   [        R                  S9n
WR#                  U	0 5      nXS.U R                  U	'   U R                  R%                  U	5        Mj     ['        U5      S:  a)  [        R                   " U[        R                  S9U l
        g[        R                  " S[        R                  5      U l
        g! , (       d  f       GN
= f)z7Load from {path}.npz and {path}.json and rebuild cache.Nr   r   r   r   T)allow_pickler   r   rr   r   ro   r   )re   osrT   existsrg   clearrh   r_   r   rq   ri   loadtolistr   r   	enumerateasarrayr;   rr   r   )r   npz_path	json_pathdatar   embsr   r   r   r   embmds               r   r]   InMemoryStore.load_from_file   s   ~~nn%T*~~&e,	ww~~h''rww~~i/H/HKKDM!xxbjjADwwxd35k  "L!)S73qIIaLE 4 	nFA**TWBJJ7C3#B-0ADKKMM  %	 % BESA2::d"**=SUS[S[\bdfdndnSo 43s   ;H
H$)ri   rh   rg   re   r   )r   N)r    r!   r"   r#   r$   r   r%   rj   r_   r`   r   r	   r8   r
   r;   rB   rG   r   rJ   ra   rM   r   r   r'   rR   rY   r]   r)   r   r   r   rc   rc   ]   s2   (3-   xPTUXZ]U]P^G_ ko 3c 3eBJJS#X,F&G 3s  S T 2s 2 2s 2c 2A8E#rzz4S>*I$JK A s  t4
Abjj 
AS 
AT%U
BS=T 
A>&pr   rc   c                   T   \ rS rSrSS\S\\   S\\   4S jjr  SS\	R                  S\S	\S
\4S jjr  SS\S\S\S	\S
\4
S jjrS\S\S\S
S4S jrSS\S\S\S
\4S jjrS\S\S
\	R                  4S jr\S\	R                  S
\	R                  4S j5       r\S
\4S j5       rSrg)SpeakerRegistry   Nmatch_thresholdstorere   c                     [        U5      U l         [        5       U l        U=(       d	    [        US9U l        g !   S U l        [        R                  S5         N8= f)NzBSpeakerRegistry doesn't work because resemblyzer is not installed.)re   )r'   r   r   _encloggerwarningrc   rg   )r   r   r   re   s       r   rj   SpeakerRegistry.__init__   sM    $_5	a$DI ).(S1S	aDINN_`s	   9 Ar6   rP   candidate_min_simr   c                    U R                  U5      nU R                  R                  5       S:X  a>  U R                  5       nU R                  R	                  XT0 S9  [        [        US0 SS9/ S9$ U R                  R                  U[        SU5      5      nUS   u  pxXR                  :  a  U R                  R                  U5      u  p[        U[        U5      [        U
5      SS9nUSS	  VVs/ s H=  u  pX:  d  M  [        U[        U5      U R                  R                  U5      S   SS9PM?     nnn[        XS9$ U R                  5       nU R                  R	                  XT0 S9  U VVs/ s H=  u  pX:  d  M  [        U[        U5      U R                  R                  U5      S   SS9PM?     nnn[        [        US0 SS9US9$ s  snnf s  snnf )
z
Return Top-K nearest speakers and encode decision as chosen.is_new.
Threshold applies ONLY to Top-1. Candidates are for reference.
r   )r   g      ?T)r   r0   r   FN)
_normalizerg   rM   _new_speaker_idr8   r+   r   rR   r   r   r;   r'   dict)r   r6   rP   r   qnew_sidtopkbest_sidbest_sim_r   r-   r   simothersrefss                   r   match_topk_from_embedding)SpeakerRegistry.match_topk_from_embedding   s    OOI&;;!#**,GKKwB7" #r$? 
 )-(C(CAs1ay(Q!!W+++KKOOH-EAxx$r(5QF #'qr("*JS+ R	#uSz4;;??3+?+B5Q"*  
 #&DD &&(73 #
"
' NIc5:t{{s';A'>uM" 	 

 Wc2d;
 	

s   -G<5G,G;5Gaudio_bytessample_ratec                 F    U R                  X5      nU R                  UUUS9$ )z2Top-K matching directly from raw PCM (int16 mono).)rP   r   )
_embed_pcmr   )r   r   r   rP   r   r   s         r   match_topk_from_pcm#SpeakerRegistry.match_topk_from_pcm#  s3     ook7--/ . 
 	
r   r   r>   r?   c                 <    U R                   R                  XU5        g r   )rg   rB   rA   s       r   rB   SpeakerRegistry.set_metadata2  s      %8r   rD   c                 :    U R                   R                  XU5      $ r   )rg   rG   rF   s       r   rG   SpeakerRegistry.get_metadata5  s    {{''
AAr   c                    [         R                  " U[         R                  S9nUR                  [         R                  SS9S-  n[        XBS9nU R                  R                  U5      nUR                  [         R                  SS9$ )zHConvert raw PCM (int16 mono) to float32 waveform, preprocess, and embed.r   Frm   g      @)	source_sr)r_   
frombufferint16rp   rq   r   r   embed_utterance)r   r   r   wav_i16wav_f32wav_procr   s          r   r   SpeakerRegistry._embed_pcm8  sf    --288<..%.87B!'Aii''1zz"**5z11r   vc                     U R                  [        R                  SS9n [        R                  R	                  U 5      S-   nX-  $ )NFrm   g&.>)rp   r_   rq   linalgnorm)r   ns     r   r   SpeakerRegistry._normalize@  s6    HHRZZeH,IINN1$ur   c                  L    S[         R                  " 5       R                  S S  3$ )Nspk_   )uuiduuid4hexr   r   r   r   SpeakerRegistry._new_speaker_idF  s#    djjl&&s+,--r   )r   rg   r   )g
ףp=
?NN)   g        r   )r    r!   r"   r#   r'   r   r4   r%   rj   r_   r`   ra   r+   r   bytesr   r	   rB   rG   r   staticmethodr   r   r)   r   r   r   r   r      sL   T TXFV=W Tkstwkx T #&	.
::.
 .
 !	.

 
.
h #&

 
 	

 !
 

9s 9 9S 9T 9Bs B Bs Bc B2e 2# 2"** 2 bjj RZZ  
 .S . .r   r   )abcr   r   dataclassesr   r   r   loggingr   typingr   r   r	   r
   r   r   r   numpyr_   resemblyzerr   r   	getLoggerr    r   r   r+   r4   rc   r   r   r   r   <module>r
     s    # )   	 = =  	8 
		8	$    
 
 
&)"s )"XIp$ IpX_. _.}	s   B B