
    $jP                        d Z ddlZddlZddlZddlZddlmZmZ ddlm	Z	 ddl
Z
ddlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2  ej3        e4          Z5 G d de          Z6 G d	 d
e6          Z7 G d de6          Z8 G d d          Z9 G d de6          Z: G d de6          Z; G d de6          Z< G d de6          Z= G d de6          Z>de?de@de?fdZA G d de6          ZB G d d e6          ZC G d! d"e6          ZD G d# d$e6          ZEde6fd%ZFdS )&z
Cross-encoder abstraction for reranking.

Provides an interface for reranking with different backends.

Configuration via environment variables - see hindsight_api.config for all env var names.
    N)ABCabstractmethod)ThreadPoolExecutor   )'DEFAULT_LITELLM_API_BASEDEFAULT_RERANKER_COHERE_MODEL$DEFAULT_RERANKER_FLASHRANK_CACHE_DIR(DEFAULT_RERANKER_FLASHRANK_CPU_MEM_ARENA DEFAULT_RERANKER_FLASHRANK_MODELDEFAULT_RERANKER_GOOGLE_MODEL+DEFAULT_RERANKER_LITELLM_MAX_TOKENS_PER_DOCDEFAULT_RERANKER_LITELLM_MODEL"DEFAULT_RERANKER_LITELLM_SDK_MODEL!DEFAULT_RERANKER_LOCAL_BATCH_SIZE DEFAULT_RERANKER_LOCAL_FORCE_CPU%DEFAULT_RERANKER_LOCAL_MAX_CONCURRENTDEFAULT_RERANKER_LOCAL_MODEL(DEFAULT_RERANKER_LOCAL_TRUST_REMOTE_CODEDEFAULT_RERANKER_PROVIDER%DEFAULT_RERANKER_SILICONFLOW_BASE_URL"DEFAULT_RERANKER_SILICONFLOW_MODELDEFAULT_RERANKER_TEI_BATCH_SIZE!DEFAULT_RERANKER_TEI_HTTP_TIMEOUT#DEFAULT_RERANKER_TEI_MAX_CONCURRENT"DEFAULT_RERANKER_ZEROENTROPY_MODELENV_RERANKER_COHERE_API_KEYENV_RERANKER_COHERE_MODEL ENV_RERANKER_FLASHRANK_CACHE_DIR$ENV_RERANKER_FLASHRANK_CPU_MEM_ARENAENV_RERANKER_FLASHRANK_MODELENV_RERANKER_GOOGLE_PROJECT_ID ENV_RERANKER_LITELLM_SDK_API_KEYENV_RERANKER_LOCAL_FORCE_CPU!ENV_RERANKER_LOCAL_MAX_CONCURRENTENV_RERANKER_LOCAL_MODEL$ENV_RERANKER_LOCAL_TRUST_REMOTE_CODEENV_RERANKER_PROVIDER ENV_RERANKER_SILICONFLOW_API_KEYENV_RERANKER_TEI_BATCH_SIZEENV_RERANKER_TEI_HTTP_TIMEOUTENV_RERANKER_TEI_MAX_CONCURRENTENV_RERANKER_TEI_URL ENV_RERANKER_ZEROENTROPY_API_KEYc                       e Zd ZdZeedefd                        Zedd            Zede	e
eef                  de	e         fd            ZdS )	CrossEncoderModelz
    Abstract base class for cross-encoder reranking.

    Cross-encoders take query-document pairs and return relevance scores.
    returnc                     dS )zFReturn a human-readable name for this provider (e.g., 'local', 'tei').N selfs    j/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight_api/engine/cross_encoder.pyprovider_namezCrossEncoderModel.provider_nameF   s	     	    Nc                 
   K   dS )z
        Initialize the cross-encoder model asynchronously.

        This should be called during startup to load/connect to the model
        and avoid cold start latency on first predict() call.
        Nr2   r3   s    r5   
initializezCrossEncoderModel.initializeL   s       	r7   pairsc                 
   K   dS )z
        Score query-document pairs for relevance.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores (higher = more relevant)
        Nr2   r4   r:   s     r5   predictzCrossEncoderModel.predictV   s       	r7   r0   N)__name__
__module____qualname____doc__propertyr   strr6   r9   listtuplefloatr=   r2   r7   r5   r/   r/   ?   s          s    ^ X    ^ 
4c3h#8 
T%[ 
 
 
 ^
 
 
r7   r/   c                      e Zd ZU dZdZedz  ed<   dZeed<   dddddde	fde
dz  ded	ed
edededefdZede
fd            ZddZdeee
e
f                  dee         fdZdeee
e
f                  dee         fdZdS )LocalSTCrossEncodera  
    Local cross-encoder implementation using SentenceTransformers.

    Call initialize() during startup to load the model and avoid cold starts.

    Default model is cross-encoder/ms-marco-MiniLM-L-6-v2:
    - Fast inference (~80ms for 100 pairs on CPU)
    - Small model (80MB)
    - Trained for passage re-ranking

    Uses a dedicated thread pool to limit concurrent CPU-bound work.
    N	_executor   _max_concurrentF
model_namemax_concurrent	force_cputrust_remote_codefp16bucket_batching
batch_sizec                     |pt           | _        || _        || _        || _        || _        || _        d| _        |t          _	        dS )a  
        Initialize local SentenceTransformers cross-encoder.

        Args:
            model_name: Name of the CrossEncoder model to use.
                       Default: cross-encoder/ms-marco-MiniLM-L-6-v2
            max_concurrent: Maximum concurrent reranking calls (default: 2).
                           Higher values may cause CPU thrashing under load.
            force_cpu: Force CPU mode for local inference.
                      Default: False
            trust_remote_code: Allow loading models with custom code (security risk).
                              Required for some models like jina-reranker-v2-base-multilingual.
                              Default: False (disabled for security)
            fp16: Use FP16 (half precision) inference. Faster on MPS and CUDA,
                  may be slower on CPU. Default: False (opt-in via env var).
            bucket_batching: Sort pairs by token length before batching to reduce
                            padding waste. 36-54% speedup, quality-identical.
                            Default: False (opt-in via env var).
            batch_size: Batch size for predict() calls. Optimal values vary by
                       hardware and model (MPS: 32, CUDA: 128+). Default: 32.
        N)
r   rM   rO   rP   rQ   rR   rS   _modelrI   rL   )r4   rM   rN   rO   rP   rQ   rR   rS   s           r5   __init__zLocalSTCrossEncoder.__init__v   sL    > %D(D"!2	.$.<+++r7   r0   c                     dS )Nlocalr2   r3   s    r5   r6   z!LocalSTCrossEncoder.provider_name   s    wr7   c                 B  K   | j         dS 	 ddlm} n# t          $ r t          d          w xY wt                              d| j                    ddl}| j        rd}t                              d           nd}	 |j	        
                                p2t          |j        d          o|j        j        
                                }|rd}n4# t          $ r'}t                              d	|            Y d}~nd}~ww xY w	 ddlmc mc m} dd
lm} t          |d          s0t+          |d|j                   t                              d           n# t          $ r Y nw xY wt/          j                    5  t/          j        dt4                     t/          j        dd           t/          j        dd           t7          j        d          }|j        }	|                    t6          j                   	  || j        |ddi| j                   | _         |                    |	           n# |                    |	           w xY w	 ddd           n# 1 swxY w Y   | j!        r>|dk    r8| j         j"        #                                 t                              d           tH          j%        OtM          tH          j'        d          tH          _%        t                              dtH          j'         d           dS t                              d           dS )z9Load the cross-encoder model and initialize the executor.Nr   )CrossEncoderzmsentence-transformers is required for LocalSTCrossEncoder. Install it with: pip install sentence-transformersz1Reranker: initializing local provider with model cpuzEReranker: forcing CPU mode (HINDSIGHT_API_RERANKER_LOCAL_FORCE_CPU=1)mpsz/Failed to detect GPU/MPS, falling back to CPU: )XLMRobertaEmbeddings"create_position_ids_from_input_idszFReranker: applied transformers 5.x compatibility patch for XLM-RoBERTaignore)categoryz%.*was not found in model state dict.*)messagez.*UNEXPECTED.*transformerslow_cpu_mem_usageF)devicemodel_kwargsrP   z Reranker: FP16 inference enabledrerankermax_workersthread_name_prefixz5Reranker: local provider initialized (max_concurrent=)z>Reranker: local provider initialized (using existing executor))(rU   sentence_transformersrZ   ImportErrorloggerinforM   torchrO   cudais_availablehasattrbackendsr\   	Exceptionwarning4transformers.models.xlm_roberta.modeling_xlm_robertamodelsxlm_robertamodeling_xlm_robertar]   setattrr^   warningscatch_warningsfilterwarningsUserWarninglogging	getLoggerlevelsetLevelERRORrP   rQ   modelhalfrI   rJ   r   rL   )
r4   rZ   ro   rd   has_gpue
xlm_moduler]   transformers_loggeroriginal_levels
             r5   r9   zLocalSTCrossEncoder.initialize   s     ;"F	::::::: 	 	 	E  	 	YYYZZZ 	 > 	VFKK_````
 FV*1133 ENE22Xu~7I7V7V7X7X   "!F V V VTQRTTUUUUUUUUV	UUUUUUUUUUUUaaaaaa:'KLL f8(K  
 deee 	 	 	D	 $&& 	= 	=#H{CCCC#H6]^^^^#H6FGGGG #*"3N"C"C06N((777	=*lO!"5u!=&*&<	   $,,^<<<<#,,^<<<<<'	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	=. 9 	<5K""$$$KK:;;; (0,>/?#-- - -) KKvPcPsvvvwwwwwKKXYYYYYs_    .>AC 
D C;;D AE 
E$#E$:BI= H3I3I

II!Ir:   c                   	 ddl }| j        rt                    dk    rfdt          t                              D             	t	          t          t                              	fd          }fd|D             }| j                            || j        d	          }t          |d
          r|	                                nt          |          }dgt                    z  }t          |          D ]\  }}||         ||<   |S | j                            | j        d	          }t          |d
          r|	                                nt          |          S )a,  Synchronous prediction wrapper for thread pool execution.

        Supports two optimizations (controlled via .env):
        - bucket_batching: sort pairs by token length to reduce padding waste (36-54% speedup)
        - batch_size: explicit batch size for predict() calls (MPS optimal: 32)
        r   N   c                 ~    g | ]9}t          |         d                    t          |         d                   z   :S )r   r   len.0ir:   s     r5   
<listcomp>z5LocalSTCrossEncoder._predict_sync.<locals>.<listcomp>  s>    VVVqs58A;''#eAhqk*:*::VVVr7   c                     |          S Nr2   )r   lengthss    r5   <lambda>z3LocalSTCrossEncoder._predict_sync.<locals>.<lambda>  s    WQZ r7   )keyc                      g | ]
}|         S r2   r2   r   s     r5   r   z5LocalSTCrossEncoder._predict_sync.<locals>.<listcomp>  s    ===E!H===r7   F)rS   show_progress_bartolist        )numpyrR   r   rangesortedrU   r=   rS   rr   r   rE   	enumerate)
r4   r:   npsorted_indicessorted_pairssorted_scoresscoresnew_posorig_idxr   s
    `       @r5   _predict_syncz!LocalSTCrossEncoder._predict_sync  ss    	 	CJJNN WVVVE#e**DUDUVVVG#E#e**$5$5;O;O;O;OPPPN====n===L K//lq/rrM6=mX6V6VoM00222\`an\o\oM USZZ'F%.~%>%> : :!#0#9x  M$$UtZ_$``")&(";";Mv}}fMr7   c                    K   | j         t          d          t          j                    }|                    t
          j        | j        |           d{V S )a/  
        Score query-document pairs for relevance.

        Uses a dedicated thread pool with limited workers to prevent CPU thrashing.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores (raw logits from the model)
        N2Reranker not initialized. Call initialize() first.)rU   RuntimeErrorasyncioget_event_looprun_in_executorrI   rJ   r   r4   r:   loops      r5   r=   zLocalSTCrossEncoder.predict!  st       ;STTT %'')))
 
 
 
 
 
 
 
 	
r7   r>   )r?   r@   rA   rB   rJ   r   __annotations__rL   intr   rD   boolrV   rC   r6   r9   rE   rF   rG   r   r=   r2   r7   r5   rI   rI   d   sq          ,0I!D(///OS "&"' %;&= &=$J&= &= 	&=
  &= &= &= &= &= &= &=P s    X`Z `Z `Z `ZDN4c3h#8 NT%[ N N N N:
4c3h#8 
T%[ 
 
 
 
 
 
r7   rI   c                      e Zd ZU dZdZej        dz  ed<   eZ	e
ed<   deeddfded	ed
e
de
de
defdZedefd            Zdej        dej        dededej        f
dZddZdej        dej        dedee         deee
ef                  f
dZdeeeef                  dee         fdZdeeeef                  dee         fdZdS )RemoteTEICrossEncodera  
    Remote cross-encoder implementation using HuggingFace Text Embeddings Inference (TEI) HTTP API.

    TEI supports reranking via the /rerank endpoint.
    See: https://github.com/huggingface/text-embeddings-inference

    Note: The TEI server must be running a cross-encoder/reranker model.

    Requests are made in parallel with configurable batch size and max concurrency (backpressure).
    Uses a GLOBAL semaphore to limit concurrent requests across ALL recall operations.
    N_global_semaphore_global_max_concurrentg      >@         ?base_urltimeoutrS   rN   max_retriesretry_delayc                 ,   |                     d          | _        || _        || _        || _        || _        || _        d| _        d| _        t          j
        t          j        |k    r,|t          _        t          j        |          t          _
        dS dS )a  
        Initialize remote TEI cross-encoder client.

        Args:
            base_url: Base URL of the TEI server (e.g., "http://localhost:8080")
            timeout: Request timeout in seconds (default: 30.0)
            batch_size: Maximum batch size for rerank requests (default: 128)
            max_concurrent: Maximum concurrent requests for backpressure (default: 8).
                           This is a GLOBAL limit across all parallel recall operations.
            max_retries: Maximum number of retries for failed requests (default: 3)
            retry_delay: Initial delay between retries in seconds, doubles each retry (default: 0.5)
        /N)rstripr   r   rS   rN   r   r   _async_client	_model_idr   r   r   r   	Semaphore)r4   r   r   rS   rN   r   r   s          r5   rV   zRemoteTEICrossEncoder.__init__J  s    * !,,$,&&7;%) "3;$;~MM;I!86=6G6W6W!333 NMr7   r0   c                     dS )Nteir2   r3   s    r5   r6   z#RemoteTEICrossEncoder.provider_namep      ur7   client	semaphoremethodurlc                   K   d}| j         }|4 d{V  t          | j        dz             D ]t}	 |dk    r |j        |fi | d{V }	n |j        |fi | d{V }	|	                                 |	c cddd          d{V  S # t          j        t          j        t          j	        f$ rh}
|
}|| j        k     rQt                              d|dz    d| j        dz    d|
 d| d	           t          j        |           d{V  |d	z  }Y d}
~
d}
~
wt          j        $ r{}
|
j        j        d
k    r_|| j        k     rT|
}t                              d|dz    d| j        dz    d|
 d| d	           t          j        |           d{V  |d	z  }n Y d}
~
nd}
~
ww xY w	 ddd          d{V  n# 1 d{V swxY w Y   |)zeMake an async HTTP request with automatic retries on transient errors and semaphore for backpressure.Nr   GETzTEI request failed (attempt r   z): z. Retrying in zs...r   i  zTEI server error (attempt )r   r   r   getpostraise_for_statushttpxConnectErrorReadTimeoutWriteTimeoutrm   ru   r   sleepHTTPStatusErrorresponsestatus_code)r4   r   r   r   r   kwargs
last_errordelayattemptr   r   s              r5   _async_request_with_retryz/RemoteTEICrossEncoder._async_request_with_retryt  si      
  	 	 	 	 	 	 	 	 !1A!566  )3C)B)B6)B)B#B#B#B#B#B#B)4S)C)CF)C)C#C#C#C#C#C#C--///#OO	 	 	 	 	 	 	 	 	 	 	 	 	 	 *E,=u?QR # # #!"J!11177Q; 7 7IY\]I] 7 7bc 7 7+07 7 7   &mE222222222
,   z-444CS9S9S%&
71 7 7tGWZ[G[ 7 7`a 7 7+07 7 7   &mE222222222
 #	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	> sO   F5AB	4F5	&F/ADF5F$A0FF5FF55
F?F?c           	      T  K   | j         dS t                              d| j         d| j         d| j         d           t          j        | j                  | _         t          j
        d          }	 |                     | j         |d| j         d	           d{V }|                                }|                    d
d          | _        t                              d| j         d           dS # t          j        $ r&}d| _         t!          d| j         d|           d}~ww xY w)z:Initialize the HTTP client and verify server connectivity.Nz'Reranker: initializing TEI provider at z (batch_size=z, max_concurrent=rj   r   r   r   z/infomodel_idunknownz+Reranker: TEI provider initialized (model: z#Failed to connect to TEI server at z: )r   rm   rn   r   rS   rN   r   AsyncClientr   r   r   r   jsonr   r   	HTTPErrorr   )r4   init_semaphorer   rn   r   s        r5   r9   z RemoteTEICrossEncoder.initialize  s~     )FTdm T T?T T=A=PT T T	
 	
 	
 #.t|DDD !*1--		[!;;"NEdm;R;R;R       H ==??D!XXj)<<DNKKWdnWWWXXXXX 	[ 	[ 	[!%DYT]YYVWYYZZZ	[s   3A=C2 2D'!D""D'querytextsc           
         K   	 |                      ||d| j         d||dd           d{V }|                                }d |D             S # t          j        $ r}t          d|           d}~ww xY w)	zNRerank a single query group and return list of (original_index, score) tuples.POST/rerankF)r   r   return_textr   Nc                 .    g | ]}|d          |d         fS )indexscorer2   )r   results     r5   r   z=RemoteTEICrossEncoder._rerank_query_group.<locals>.<listcomp>  s%    MMM6VG_fWo6MMMr7   zTEI rerank request failed: )r   r   r   r   r   r   )r4   r   r   r   r   r   resultsr   s           r5   _rerank_query_groupz)RemoteTEICrossEncoder._rerank_query_group  s      	B!;;=)))""#(  < 
 
 
 
 
 
 
 
H mmooGMMWMMMM 	B 	B 	B@Q@@AAA	Bs   AA A6A11A6r:   c                    K   |sg S i }t          |          D ].\  }\  }}||vrg ||<   ||                             ||f           /g }|                                D ]\  }}d |D             }d |D             }	t          dt	          |	           j                  D ]>}
||
|
 j        z            }|	|
|
 j        z            }|                    |||f           ?dgt	          |          z  }t          j         fd|D             }t          j	        |  d{V }t          ||          D ]\  \  }}}}|D ]\  }}||         }|||<   |S )zQAsync implementation of predict that runs requests in parallel with backpressure.c                     g | ]\  }}|S r2   r2   r   idx_s      r5   r   z8RemoteTEICrossEncoder._predict_async.<locals>.<listcomp>      777vsAs777r7   c                     g | ]\  }}|S r2   r2   r   r   texts      r5   r   z8RemoteTEICrossEncoder._predict_async.<locals>.<listcomp>      777gaT777r7   r   r   c                 R    g | ]#\  }}}                     j        ||          $S r2   )r   r   )r   r   r   r   r4   r   s       r5   r   z8RemoteTEICrossEncoder._predict_async.<locals>.<listcomp>  sF     
 
 
VeV[]^`eD$$T%7E5QQ
 
 
r7   N)r   appenditemsr   r   rS   r   r   r   gatherzip)r4   r:   query_groupsr   r   r   
tasks_infoindexed_textsindicesr   r   batch_indicesbatch_texts
all_scorestasksr   r   result_scoresoriginal_idx_in_batchr   
global_idxr   s   `                    @r5   _predict_asyncz$RemoteTEICrossEncoder._predict_async  s      	I :<"+E"2"2 	4 	4C%L((&(U#&&T{3333 >@
$0$6$6$8$8 	G 	G E=77777G77777E 1c%jj$/:: G G 'A,?(? @#ADO(;$;<!!5-"EFFFFG USZZ'
);	
 
 
 
 
is
 
 
  ....... /2*g.F.F 	/ 	/*OQ]0= / /,%u$%:;
).
:&&/ r7   c                 h   K   | j         t          d          |                     |           d{V S )a  
        Score query-document pairs using the remote TEI reranker.

        Requests are made in parallel with configurable backpressure.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores
        Nr   )r   r   r  r<   s     r5   r=   zRemoteTEICrossEncoder.predict  sF       %STTT((/////////r7   r>   )r?   r@   rA   rB   r   r   r   r   r   r   r   r   rD   rG   rV   rC   r6   r   r   Responser   r9   rE   rF   r   r  r=   r2   r7   r5   r   r   9  s        
 
 37w(4/666"ECEEE
 9A $X $X$X $X 	$X
 $X $X $X $X $X $XL s    X+!+ $+ 	+
 + 
+ + + +Z[ [ [ [2B!B $B 	B
 CyB 
eCJ	 B B B B4($uS#X*? (DK ( ( ( (T04c3h#8 0T%[ 0 0 0 0 0 0r7   r   c                   t    e Zd ZdZ	 	 ddededededef
d	ZddZde	e
eef                  d
e	e         fdZdS )_CohereCompatibleRerankClientu  
    Internal HTTP client for Cohere-compatible /rerank endpoints.

    Shared by all providers that speak the Cohere rerank wire format —
    {model, query, documents[, top_n]} request and
    {results: [{index, relevance_score}, ...]} response. This covers
    SiliconFlow, ZeroEntropy, Jina, Voyage, BGE self-hosted, and Cohere
    itself when reached via a custom base_url (e.g. Azure AI Foundry).

    Not a CrossEncoderModel — providers compose it and expose their own
    provider_name / initialization logging.
          N@Tapi_keyr   
rerank_urlr   include_top_nc                 Z    || _         || _        || _        || _        || _        d | _        d S r   )r  r   r  r   r  r   )r4   r  r   r  r   r  s         r5   rV   z&_CohereCompatibleRerankClient.__init__  s6     
$*7;r7   r0   Nc                 r   K   | j         d S t          j        | j        d| j         dd          | _         d S )NBearer application/json)AuthorizationContent-Typer   headers)r   r   r   r   r  r3   s    r5   r9   z(_CohereCompatibleRerankClient.initialize-  sS      )F".L!94<!9!9 2 
 
 
r7   r:   c                   K   | j         t          d          |sg S i }t          |          D ]3\  }\  }}|                    |g                               ||f           4dgt          |          z  }|                                D ]\  }}d |D             }d |D             }	| j        ||dd}
| j        rt          |          |
d<   | j         	                    | j
        |
           d {V }|                                 |                                }|                    d	g           D ]}|d
         }|d         }|||	|         <   |S )Nr   r   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z9_CohereCompatibleRerankClient.predict.<locals>.<listcomp>F  r   r7   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z9_CohereCompatibleRerankClient.predict.<locals>.<listcomp>G  r   r7   F)r   r   	documentsreturn_documentstop_nr   r   r   relevance_score)r   r   r   
setdefaultr   r   r   r   r  r   r  r   r   r   )r4   r:   r  r   r   r   r  r  r   r  bodyr   r   itemoriginal_idxr   s                   r5   r=   z%_CohereCompatibleRerankClient.predict8  s     %STTT 	I9;"+E"2"2 	C 	CC%##E2..55sDkBBBBUSZZ'
$0$6$6$8$8 	: 	: E=77777E77777G "$)	' 'D ! + #E

W!/44T_44PPPPPPPPH%%''']]__F

9b11 : :#G}./49
7<011:
 r7   )r  Tr>   )r?   r@   rA   rB   rD   rG   r   rV   r9   rE   rF   r=   r2   r7   r5   r  r    s         $ "< << < 	<
 < < < < <	
 	
 	
 	
#4c3h#8 #T%[ # # # # # #r7   r  c            	           e Zd ZdZeddfdedededz  defdZed	efd
            Z	ddZ
deeeef                  d	ee         fdZdeeeef                  d	ee         fdZdS )CohereCrossEncoderz
    Cohere cross-encoder implementation using the Cohere Rerank API.

    Supports rerank-english-v3.0 and rerank-multilingual-v3.0 models.
    Nr  r  r   r   r   c                     || _         || _        || _        || _        d| _        |rt          ||||d          nd| _        dS )aU  
        Initialize Cohere cross-encoder client.

        Args:
            api_key: Cohere API key
            model: Cohere rerank model name (default: rerank-english-v3.0)
            base_url: Custom base URL for Cohere-compatible API (e.g., Azure-hosted endpoint)
            timeout: Request timeout in seconds (default: 60.0)
        NF)r  r   r  r   r  )r  r   r   r   _clientr  _http_clientr4   r  r   r   r   s        r5   rV   zCohereCrossEncoder.__init__e  sj      
  )##     	r7   r0   c                     dS )Ncoherer2   r3   s    r5   r6   z CohereCrossEncoder.provider_name      xr7   c                   K   | j         | j        r| j        j        rdS | j        r
d| j         nd}t                              d| j         |            | j        ;| j                                         d{V  t                              d           dS 	 ddl}n# t          $ r t          d          w xY w|
                    | j        | j                  | _         t                              d	           dS )
zInitialize the Cohere client.N at  z2Reranker: initializing Cohere provider with model zGReranker: Cohere provider initialized (Cohere-compatible HTTP endpoint)r   zNcohere is required for CohereCrossEncoder. Install it with: pip install cohere)r  r   z%Reranker: Cohere provider initialized)r+  r,  r   r   rm   rn   r   r9   r/  rl   Clientr  r   )r4   base_url_msgr/  s      r5   r9   zCohereCrossEncoder.initialize  s#     <#(9#d>O>]#F15F-dm---BccUaccddd(#..000000000KKabbbbbt t t t!"rssst "==t|=TTDLKK?@@@@@s   B B8r:   c                    K   | j         | j        t          d          |sg S | j         | j                            |           d{V S t	          j                    }|                    d| j        |           d{V S )z
        Score query-document pairs using the Cohere Rerank API.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores
        Nr   )r+  r,  r   r=   r   r   r   _predict_sync_sdkr   s      r5   r=   zCohereCrossEncoder.predict  s       <D$5$=STTT 	I(*225999999999 %''))$0FNNNNNNNNNr7   c                    i }t          |          D ]3\  }\  }}|                    |g                               ||f           4dgt          |          z  }|                                D ]c\  }}d |D             }d |D             }	| j                            ||| j        d          }
|
j        D ]}|j	        }|j
        }|||	|         <   d|S )z0Synchronous predict using the native Cohere SDK.r   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z8CohereCrossEncoder._predict_sync_sdk.<locals>.<listcomp>  r   r7   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z8CohereCrossEncoder._predict_sync_sdk.<locals>.<listcomp>  r   r7   F)r   r   r   r!  )r   r$  r   r   r   r+  rerankr   r   r   r#  )r4   r:   r  r   r   r   r  r  r   r  r   r   r'  r   s                 r5   r7  z$CohereCrossEncoder._predict_sync_sdk  s   9;"+E"2"2 	C 	CC%##E2..55sDkBBBBUSZZ'
$0$6$6$8$8 	: 	: E=77777E77777G|**j!&	 +  H #* : :%|.49
7<011:
 r7   r>   )r?   r@   rA   rB   r   rD   rG   rV   rC   r6   r9   rE   rF   r=   r7  r2   r7   r5   r)  r)  ^  s         3#"
 "
"
 "
 *	"

 "
 "
 "
 "
H s    XA A A A*O4c3h#8 OT%[ O O O O.tE#s(O'< e      r7   r)  c            	           e Zd ZdZdZdZeddfdedededz  d	efd
Z	e
defd            ZddZdeeeef                  dee         fdZdS )ZeroEntropyCrossEncoderz
    ZeroEntropy cross-encoder implementation using the ZeroEntropy Rerank API.

    Supports zerank-2 (flagship) and zerank-2-small models.
    See: https://docs.zeroentropy.dev/models
    zhttps://api.zeroentropy.devz/v1/models/rerankNr  r  r   r   r   c                     || _         |r|                    d          n| j        | _        t	          ||| j         | j         |          | _        d S Nr   )r  r   r  r   )r   r   DEFAULT_BASE_URLr   r  RERANK_PATHr+  r-  s        r5   rV   z ZeroEntropyCrossEncoder.__init__  sa     
08S,,,d>S4-;)9;;	
 
 
r7   r0   c                     dS )Nzeroentropyr2   r3   s    r5   r6   z%ZeroEntropyCrossEncoder.provider_name      }r7   c                    K   | j         j        d S t                              d| j                    | j                                          d {V  t                              d           d S )Nz7Reranker: initializing ZeroEntropy provider with model z*Reranker: ZeroEntropy provider initialized)r+  r   rm   rn   r   r9   r3   s    r5   r9   z"ZeroEntropyCrossEncoder.initialize  sr      <%1FZdjZZ[[[l%%'''''''''@AAAAAr7   r:   c                 F   K   | j                             |           d {V S r   r+  r=   r<   s     r5   r=   zZeroEntropyCrossEncoder.predict  .      \))%000000000r7   r>   )r?   r@   rA   rB   r@  rA  r   rD   rG   rV   rC   r6   r9   rE   rF   r=   r2   r7   r5   r=  r=    s          5%K
 8#
 

 
 *	

 
 
 
 
  s    XB B B B14c3h#8 1T%[ 1 1 1 1 1 1r7   r=  c            	           e Zd ZdZdZeedfdedededefdZ	e
d	efd
            ZddZdeeeef                  d	ee         fdZdS )SiliconFlowCrossEncoderz
    SiliconFlow cross-encoder implementation.

    SiliconFlow (https://siliconflow.cn) exposes a Cohere-compatible /rerank
    endpoint. Shares the HTTP client with ZeroEntropy/Cohere-custom-endpoint
    via _CohereCompatibleRerankClient.
    r   r  r  r   r   r   c                     || _         |                    d          | _        t          ||| j         | j         |          | _        d S r?  )r   r   r   r  rA  r+  r-  s        r5   rV   z SiliconFlowCrossEncoder.__init__	  sT     
 ,,4-;)9;;	
 
 
r7   r0   c                     dS )Nsiliconflowr2   r3   s    r5   r6   z%SiliconFlowCrossEncoder.provider_name  rD  r7   Nc                    K   | j         j        d S t                              d| j         d| j                    | j                                          d {V  t                              d           d S )Nz/Reranker: initializing SiliconFlow provider at  with model z*Reranker: SiliconFlow provider initialized)r+  r   rm   rn   r   r   r9   r3   s    r5   r9   z"SiliconFlowCrossEncoder.initialize  s~      <%1Fmdmmmaeakmmnnnl%%'''''''''@AAAAAr7   r:   c                 F   K   | j                             |           d {V S r   rG  r<   s     r5   r=   zSiliconFlowCrossEncoder.predict$  rH  r7   r>   )r?   r@   rA   rB   rA  r   r   rD   rG   rV   rC   r6   r9   rE   rF   r=   r2   r7   r5   rJ  rJ    s          K
 8=
 

 
 	

 
 
 
 
  s    XB B B B14c3h#8 1T%[ 1 1 1 1 1 1r7   rJ  c                   t    e Zd ZdZd Zedefd            Zd	dZde	e
eef                  de	e         fdZdS )
RRFPassthroughCrossEncodera  
    Passthrough cross-encoder that preserves RRF scores without neural reranking.

    This is useful for:
    - Testing retrieval quality without reranking overhead
    - Deployments where reranking latency is unacceptable
    - Debugging to isolate retrieval vs reranking issues
    c                     dS )z)Initialize RRF passthrough cross-encoder.Nr2   r3   s    r5   rV   z#RRFPassthroughCrossEncoder.__init__2  s    r7   r0   c                     dS )Nrrfr2   r3   s    r5   r6   z(RRFPassthroughCrossEncoder.provider_name6  r   r7   Nc                 >   K   t                               d           dS )zNo initialization needed.zJReranker: RRF passthrough provider initialized (neural reranking disabled)N)rm   rn   r3   s    r5   r9   z%RRFPassthroughCrossEncoder.initialize:  s      `aaaaar7   r:   c                 ,   K   dgt          |          z  S )z
        Return neutral scores - actual ranking uses RRF scores from retrieval.

        Args:
            pairs: List of (query, document) tuples (ignored)

        Returns:
            List of 0.5 scores (neutral, lets RRF scores dominate)
        r   r   r<   s     r5   r=   z"RRFPassthroughCrossEncoder.predict>  s       us5zz!!r7   r>   )r?   r@   rA   rB   rV   rC   rD   r6   r9   rE   rF   rG   r=   r2   r7   r5   rR  rR  (  s            s    Xb b b b"4c3h#8 "T%[ " " " " " "r7   rR  c                       e Zd ZU dZdZedz  ed<   dZeed<   	 	 	 	 	 dde	dz  d	e	dz  d
edede
f
dZede	fd            ZddZdeee	e	f                  dee         fdZdeee	e	f                  dee         fdZdS )FlashRankCrossEncodera  
    FlashRank cross-encoder implementation.

    FlashRank is an ultra-lite reranking library that runs on CPU without
    requiring PyTorch or Transformers. It's ideal for serverless deployments
    with minimal cold-start overhead.

    Available models:
    - ms-marco-TinyBERT-L-2-v2: Fastest, ~4MB
    - ms-marco-MiniLM-L-12-v2: Best quality, ~34MB (default)
    - rank-T5-flan: Best zero-shot, ~110MB
    - ms-marco-MultiBERT-L-12: Multi-lingual, ~150MB
    NrJ   rK   rL      FrM   	cache_dir
max_lengthrN   cpu_mem_arenac                     |pt           | _        |pt          | _        || _        || _        d| _        |t          _        dS )a  
        Initialize FlashRank cross-encoder.

        Args:
            model_name: FlashRank model name. Default: ms-marco-MiniLM-L-12-v2
            cache_dir: Directory to cache downloaded models. Default: system cache
            max_length: Maximum sequence length for reranking. Default: 512
            max_concurrent: Maximum concurrent reranking calls. Default: 4
            cpu_mem_arena: Enable ONNX Runtime CPU memory arena. Default: False.
                          When True, ONNX pre-allocates a memory arena that never
                          shrinks, causing RSS to grow monotonically. False trades
                          slightly slower per-call allocation for bounded RSS.
        N)	r   rM   r	   r[  r\  r]  _rankerrY  rL   )r4   rM   r[  r\  rN   r]  s         r5   rV   zFlashRankCrossEncoder.__init___  sC    * %H(H"J&J$*0>---r7   r0   c                     dS )N	flashrankr2   r3   s    r5   r6   z#FlashRankCrossEncoder.provider_name{  s    {r7   c                   K   | j         dS 	 ddlm} n# t          $ r t          d          w xY wt                              d| j         d| j         d           | j        s ddl}|	                                }d|_
        nd}| j        | j        d	}| j        r
| j        |d
<    |di || _         |t          | j         d          rddl}d}t          | j         dd          }|r6ddlm}  ||                              d          D ]}t%          |          } |r;|                    ||          | j         _        t                              d           t*          j        Ot/          t*          j        d          t*          _        t                              dt*          j         d           dS t                              d           dS )zLoad the FlashRank model.Nr   )RankerzWflashrank is required for FlashRankCrossEncoder. Install it with: pip install flashrankz5Reranker: initializing FlashRank provider with model z (cpu_mem_arena=rj   F)rM   r\  r[  session	model_dir)Pathz*.onnx)sess_optionszBReranker: replaced FlashRank ONNX session with cpu_mem_arena=Falsera  rg   z9Reranker: FlashRank provider initialized (max_concurrent=zBReranker: FlashRank provider initialized (using existing executor)r2   )r_  ra  rc  rl   rm   rn   rM   r]  onnxruntimeSessionOptionsenable_cpu_mem_arenar\  r[  rr   getattrpathlibrf  globrD   InferenceSessionrd  rY  rJ   r   rL   )	r4   rc  ortsession_optionsranker_kwargs
model_filere  rf  	candidates	            r5   r9   z FlashRankCrossEncoder.initialize  sU     <#F	y((((((( 	y 	y 	ywxxx	y 	5DO 5 5#15 5 5	
 	
 	
 ! 	#%%%%!0022O38O00"O .2_DO\\> 	8)-M+&v....
 &74<+K+K&%%%%Jk4@@I ((((((!%i!5!5h!?!?  I!$YJ b'*';';JUd';'e'e$`aaa !*2.@1A#./ / /!+ KKtLaLqttt     KK\]]]]]s    .r:   c                    ddl m} |sg S i }t          |          D ].\  }\  }}||vrg ||<   ||                             ||f           /dgt	          |          z  }|                                D ]s\  }}d t          |          D             }	d |D             }
 |||	          }| j                            |          }|D ]}|d         }|d         }|
|         }|||<    t|S )	z1Synchronous predict - processes each query group.r   )RerankRequestr   c                 $    g | ]\  }\  }}||d S ))idr   r2   )r   r   r   r   s       r5   r   z7FlashRankCrossEncoder._predict_sync.<locals>.<listcomp>  s(    ]]]LAy4q$//]]]r7   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z7FlashRankCrossEncoder._predict_sync.<locals>.<listcomp>  s    >>>fc1c>>>r7   )r   passagesrw  r   )ra  ru  r   r   r   r   r_  r;  )r4   r:   ru  r  r   r   r   r  r  ry  global_indicesrequestr   r   	local_idxr   r  s                    r5   r   z#FlashRankCrossEncoder._predict_sync  sO   ++++++ 	I :<"+E"2"2 	4 	4C%L((&(U#&&T{3333USZZ'
$0$6$6$8$8 	/ 	/ E=]]ImD\D\]]]H>>>>>N $m%(CCCGl))'22G " / /"4L	w+I6
).
:&&	/ r7   c                    K   | j         t          d          t          j                    }|                    t
          j        | j        |           d{V S )z
        Score query-document pairs using FlashRank.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores (higher = more relevant)
        Nr   )r_  r   r   r   r   rY  rJ   r   r   s      r5   r=   zFlashRankCrossEncoder.predict  sa       <STTT %''))*?*I4K]_deeeeeeeeer7   )NNrZ  rK   Fr>   )r?   r@   rA   rB   rJ   r   r   rL   r   rD   r   rV   rC   r6   r9   rE   rF   rG   r   r=   r2   r7   r5   rY  rY  L  sY          ,0I!D(///OS "& $#? ?$J? :? 	?
 ? ? ? ? ?8 s    X=^ =^ =^ =^~ 4c3h#8  T%[        Df4c3h#8 fT%[ f f f f f fr7   rY  r   
max_tokensr0   c                     ddl m}  |            }|                    |           }t          |          |k    r| S |                    |d|                   S )zFTruncate text to at most max_tokens using the shared tiktoken encoder.r   )_get_tiktoken_encodingN)memory_enginer  encoder   decode)r   r~  r  enctokenss        r5   _truncate_to_tokensr    sd    555555
 
 
"
"CZZF
6{{j  ::f[j[)***r7   c                       e Zd ZdZededefdededz  dedede	dz  f
d	Z
ed
efd            ZddZdeeeef                  d
ee         fdZdS )LiteLLMCrossEncodera+  
    LiteLLM cross-encoder implementation using LiteLLM proxy's /rerank endpoint.

    LiteLLM provides a unified interface for multiple reranking providers via
    the Cohere-compatible /rerank endpoint.
    See: https://docs.litellm.ai/docs/rerank

    Supported providers via LiteLLM:
    - Cohere (rerank-english-v3.0, etc.) - prefix with cohere/
    - Together AI - prefix with together_ai/
    - Azure AI - prefix with azure_ai/
    - Jina AI - prefix with jina_ai/
    - AWS Bedrock - prefix with bedrock/
    - Voyage AI - prefix with voyage/
    Nr  api_baser  r   r   max_tokens_per_docc                     |                     d          | _        || _        || _        || _        || _        d| _        dS )a  
        Initialize LiteLLM cross-encoder client.

        Args:
            api_base: Base URL of the LiteLLM proxy (default: http://localhost:4000)
            api_key: API key for the LiteLLM proxy (optional, depends on proxy config)
            model: Reranking model name (default: cohere/rerank-english-v3.0)
                   Use provider prefix (e.g., cohere/, together_ai/, voyage/)
            timeout: Request timeout in seconds (default: 60.0)
            max_tokens_per_doc: If set, truncate each document to this many tokens before
                                sending to the reranker (uses tiktoken cl100k_base encoding).
                                Useful for models with small context windows (e.g. 1024 tokens).
        r   N)r   r  r  r   r   r  r   )r4   r  r  r   r   r  s         r5   rV   zLiteLLMCrossEncoder.__init__  sB    * !,,
"47;r7   r0   c                     dS )Nlitellmr2   r3   s    r5   r6   z!LiteLLMCrossEncoder.provider_name*  s    yr7   c                   K   | j         dS t                              d| j         d| j                    ddi}| j        rd| j         |d<   t          j        | j        |          | _         t                              d	           dS )
z!Initialize the async HTTP client.Nz+Reranker: initializing LiteLLM provider at rO  r  r  r  r  r  z&Reranker: LiteLLM provider initialized)	r   rm   rn   r  r   r  r   r   r   )r4   r  s     r5   r9   zLiteLLMCrossEncoder.initialize.  s      )Fi$-ii]a]giijjj!#56< 	@'?'?'?GO$".t|WUUU<=====r7   r:   c           
         K    j         t          d          |sg S i }t          |          D ].\  }\  }}||vrg ||<   ||                             ||f           /dgt	          |          z  }|                                D ]\  }}d |D             } j         fd|D             }d |D             }	 j                              j         d j	        ||t	          |          d	           d{V }
|

                                 |
                                }|                    d
g           D ]?}|d         }|                    d|                    dd                    }|||	|         <   @|S )z
        Score query-document pairs using the LiteLLM proxy's /rerank endpoint.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores
        Nr   r   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z/LiteLLMCrossEncoder.predict.<locals>.<listcomp>V  r   r7   c                 :    g | ]}t          |j                  S r2   r  r  r   tr4   s     r5   r   z/LiteLLMCrossEncoder.predict.<locals>.<listcomp>X  '    XXXQ,Q0GHHXXXr7   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z/LiteLLMCrossEncoder.predict.<locals>.<listcomp>Y  r   r7   r   )r   r   r   r"  r   r   r   r#  r   )r   r   r   r   r   r   r  r   r  r   r   r   r   )r4   r:   r  r   r   r   r  r  r   r  r   r   r&  r'  r   s   `              r5   r=   zLiteLLMCrossEncoder.predict<  s      %STTT 	I :<"+E"2"2 	4 	4C%L((&(U#&&T{3333USZZ'
$0$6$6$8$8 	: 	: E=77777E&2XXXXRWXXX77777G "/44=)))!Z"!& ZZ	  5        H %%''']]__F 

9b11 : :#G}!2DHHWc4J4JKK49
7<011:
 r7   r>   )r?   r@   rA   rB   r   r   r   rD   rG   r   rV   rC   r6   r9   rE   rF   r=   r2   r7   r5   r  r    s         $ 1"3)T< << t< 	<
 <  $J< < < <8 s    X> > > >34c3h#8 3T%[ 3 3 3 3 3 3r7   r  c                       e Zd ZdZeddefdedededz  dededz  f
d	Z	e
d
efd            ZddZdeeeef                  d
ee         fdZdS )LiteLLMSDKCrossEncodera  
    LiteLLM SDK cross-encoder for direct API integration.

    Supports reranking via LiteLLM SDK without requiring a proxy server.
    Supported providers: Cohere, DeepInfra, Together AI, HuggingFace, Jina AI, Voyage AI, AWS Bedrock.

    Example model names:
    - cohere/rerank-english-v3.0
    - deepinfra/Qwen3-reranker-8B
    - together_ai/Salesforce/Llama-Rank-V1
    - huggingface/BAAI/bge-reranker-v2-m3
    Nr  r  r   r  r   r  c                 h    || _         || _        || _        || _        || _        d| _        d| _        dS )ap  
        Initialize LiteLLM SDK cross-encoder client.

        Args:
            api_key: API key for the reranking provider
            model: Model name with provider prefix (e.g., "deepinfra/Qwen3-reranker-8B")
            api_base: Custom base URL for API (optional)
            timeout: Request timeout in seconds (default: 60.0)
            max_tokens_per_doc: If set, truncate each document to this many tokens before
                                sending to the reranker (uses tiktoken cl100k_base encoding).
                                Useful for models with small context windows (e.g. 1024 tokens).
        FN)r  r   r  r   r  _initialized_litellm)r4   r  r   r  r   r  s         r5   rV   zLiteLLMSDKCrossEncoder.__init__  s;    ( 
 "4!r7   r0   c                     dS )Nlitellm-sdkr2   r3   s    r5   r6   z$LiteLLMSDKCrossEncoder.provider_name  rD  r7   c                    K   | j         rdS 	 ddl}|| _        n# t          $ r t          d          w xY w| j        r
d| j         nd}t
                              d| j         |            d| _         t
                              d           dS )	z"Initialize the LiteLLM SDK client.Nr   zTlitellm is required for LiteLLMSDKCrossEncoder. Install it with: pip install litellmr2  r3  z7Reranker: initializing LiteLLM SDK provider with model Tz*Reranker: LiteLLM SDK provider initialized)r  r  r  rl   r  rm   rn   r   )r4   r  api_base_msgs      r5   r9   z!LiteLLMSDKCrossEncoder.initialize  s       	F	vNNN#DMM 	v 	v 	vtuuu	v 26F-dm---BhdjhZfhhiii @AAAAAs    3r:   c           	      l   K    j         st          d          |sg S i }t          |          D ].\  }\  }}||vrg ||<   ||                             ||f           /dgt	          |          z  }|                                D ]-\  }}d |D             } j         fd|D             }d |D             }	 j        || j        d}
 j	        r
 j	        |
d<     j
        j        di |
 d{V }t          |d	          rO|j        rH|j        D ]?}|d
         }|                    d|                    dd                    }|||	|         <   @t          |t                     r"t          |          D ]\  }}|||	|         <   t"                              dt'          |                      /|S )z
        Score query-document pairs using the LiteLLM SDK.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores
        r   r   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z2LiteLLMSDKCrossEncoder.predict.<locals>.<listcomp>  r   r7   Nc                 :    g | ]}t          |j                  S r2   r  r  s     r5   r   z2LiteLLMSDKCrossEncoder.predict.<locals>.<listcomp>  r  r7   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z2LiteLLMSDKCrossEncoder.predict.<locals>.<listcomp>  r   r7   )r   r   r   r  r  r   r   r#  r   z0Unexpected response format from LiteLLM rerank: r2   )r  r   r   r   r   r   r  r   r  r  r  arerankrr   r   r   
isinstancerE   rm   ru   type)r4   r:   r  r   r   r   r  r  r   r  rerank_kwargsr   r   r'  r   r   s   `               r5   r=   zLiteLLMSDKCrossEncoder.predict  sV        	USTTT 	I :<"+E"2"2 	4 	4C%L((&(U#&&T{3333USZZ'
$0$6$6$8$8  	d  	d E=77777E&2XXXXRWXXX77777G "<	 M } :,0Mj)2T]2CC]CCCCCCCCH
 x++ d0@ d&. > >F#)'?L"JJ'8&**Wc:R:RSSE8=Jw|455	>
 Hd++ d )( 3 3 3 3HAu-2Jwqz**3 bRVW_R`R`bbccccr7   r>   )r?   r@   rA   rB   r   r   rD   rG   r   rV   rC   r6   r9   rE   rF   r=   r2   r7   r5   r  r  r  s           8#)T   *	
   $J   8 s    XB B B B$<4c3h#8 <T%[ < < < < < <r7   r  c                       e Zd ZdZdZddedz  fdZedefd            ZddZ	dd	Z
d
eeeef                  dee         fdZd
eeeef                  dee         fdZdS )JinaMLXCrossEncoderu  
    Jina Reranker v3 MLX implementation for Apple Silicon.

    Uses jinaai/jina-reranker-v3-mlx — a 0.6B parameter multilingual listwise reranker
    optimized for Apple Silicon via the MLX framework. No transformers/PyTorch dependency.

    The model is downloaded automatically from HuggingFace Hub on first use.
    Requires: mlx>=0.31.0, mlx-lm>=0.31.1, safetensors>=0.6.2
    zjinaai/jina-reranker-v3-mlxN
model_pathc                 "    || _         d| _        dS )z
        Args:
            model_path: Local path to the downloaded model directory.
                        If None, the model is downloaded from HuggingFace Hub.
        N)r  	_reranker)r4   r  s     r5   rV   zJinaMLXCrossEncoder.__init__  s     %r7   r0   c                     dS )Njina-mlxr2   r3   s    r5   r6   z!JinaMLXCrossEncoder.provider_name  s    zr7   c                 "  K   | j         d S dd l}|j        }	 dd l}dd l}n:# t
          $ r-}t          |          }d|vrd|vr t          d          |d }~ww xY wt          j                    }|	                    d | j
                   d {V  d S )Nr   mlxmlx_lmz|mlx and mlx-lm are required for JinaMLXCrossEncoder. Install with: pip install mlx>=0.31.0 mlx-lm>=0.31.1 safetensors>=0.6.2)r  rb   AutoTokenizermlx.corer  rl   rD   r   r   r   _load_model)r4   rb   r   r  r  excmsgr   s           r5   r9   zJinaMLXCrossEncoder.initialize  s      >%F 	&	OOOMMMM 	 	 	
 c((CCHC$7$7Z  	 %''""4)9:::::::::::s   ! 
A(AAc                    ddl }ddl}ddlm} ddlm} | j        }|4t                              d| j	         d            || j	                  }t                              d	|             |||j
                            |d
                    | _        |                                | _        t                              d           dS )zADownload (if needed) and load the MLX reranker. Runs in a thread.r   N)snapshot_downloadr   )MLXRerankerzReranker: downloading z from HuggingFace Hub...)repo_idz,Reranker: loading jina-reranker-v3-mlx from zprojector.safetensors)r  projector_pathz'Reranker: jina-mlx provider initialized)os	threadinghuggingface_hubr  jina_mlx_rerankerr  r  rm   rn   
HF_REPO_IDpathjoinr  Lock	_mlx_lock)r4   r  r  r  r  r  s         r5   r  zJinaMLXCrossEncoder._load_model,  s    			555555222222_
KKZZZZ[[[**4?CCCJO:OOPPP$!7<<
4KLL
 
 
 #))=>>>>>r7   r:   c                    |sg S i }t          |          D ]3\  }\  }}|                    |g                               ||f           4dgt          |          z  }| j        5  |                                D ]V\  }}d |D             }d |D             }	| j                            ||          }
|
D ]}|d         }|d         ||	|         <   W	 ddd           n# 1 swxY w Y   |S )z/Score pairs grouped by query. Runs in a thread.r   c                     g | ]\  }}|S r2   r2   )r   r   docs      r5   r   z5JinaMLXCrossEncoder._predict_sync.<locals>.<listcomp>R  s    7773777r7   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z5JinaMLXCrossEncoder._predict_sync.<locals>.<listcomp>S  s    :::633:::r7   r   r#  N)r   r$  r   r   r  r   r  r;  )r4   r:   r  r   r   r  r  indexed_docsdocsr  r   r   r'  s                r5   r   z!JinaMLXCrossEncoder._predict_syncE  s    	I9;!*5!1!1 	B 	BC%##E2..55sCjAAAAUSZZ'
^ 	R 	R'3'9'9';'; R R#|77,777::\:::.//t<<% R RF#)'?L8>?P8QJw|455R	R	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R s   $A,CC!$C!c                    K   | j         t          d          t          j                    }|                    d | j        |           d {V S )Nr   )r  r   r   r   r   r   r   s      r5   r=   zJinaMLXCrossEncoder.predict[  sX      >!STTT%''))$0BEJJJJJJJJJr7   r   r>   )r?   r@   rA   rB   r  rD   rV   rC   r6   r9   r  rE   rF   rG   r   r=   r2   r7   r5   r  r    s         /J 3:     s    X; ; ; ;B? ? ? ?24c3h#8 T%[    ,K4c3h#8 KT%[ K K K K K Kr7   r  c                       e Zd ZdZdZdZdgZedddfded	ed
edz  dede	f
dZ
edefd            Zdeeef         fdZddZdeeeef                  dee	         fdZdeeeef                  dee	         fdZdS )GoogleCrossEncodera  
    Google Discovery Engine cross-encoder using the Ranking REST API.

    Uses httpx + google-auth for lightweight REST calls (no gRPC/protobuf).
    Supports ADC (Application Default Credentials) or service account key file.

    Available models:
    - semantic-ranker-default-004: Best quality, 1024 tokens/record (recommended)
    - semantic-ranker-fast-004: Lower latency, 1024 tokens/record

    Max 200 records per API request. Location is always "global".
       z)https://discoveryengine.googleapis.com/v1z.https://www.googleapis.com/auth/cloud-platformNglobalr  
project_idr   service_account_keylocationr   c                 v    || _         || _        || _        || _        || _        d| _        d| _        d| _        dS )a  
        Initialize Google Discovery Engine cross-encoder.

        Args:
            project_id: Google Cloud project ID
            model: Ranking model name (default: semantic-ranker-default-004)
            service_account_key: Path to service account JSON key file.
                                If None, uses Application Default Credentials (ADC).
            location: API location (default: "global")
            timeout: Request timeout in seconds (default: 60.0)
        N)r  r   r  r  r   _credentialsr+  	_rank_url)r4   r  r   r  r  r   s         r5   rV   zGoogleCrossEncoder.__init__u  sB    & %
#6   ,0%)r7   r0   c                     dS )Ngoogler2   r3   s    r5   r6   z GoogleCrossEncoder.provider_name  r0  r7   c                     ddl }| j        j        s;| j                            |j        j        j                                                   dd| j        j         iS )z3Get Authorization header with a fresh access token.r   Nr  r  )	google.auth.transport.requestsr  validrefreshauth	transportrequestsRequesttoken)r4   r  s     r5   _get_auth_headersz$GoogleCrossEncoder._get_auth_headers  sb    ---- & 	P%%fk&;&D&L&L&N&NOOO!D4+<+B!D!DEEr7   c           	        K   | j         dS | j        sdnd}t                              d| j         d| j         d| d           | j        rQ	 dd	lm} n# t          $ r t          d
          w xY w|j	        
                    | j        | j                  | _        nK	 ddl}n# t          $ r t          d
          w xY w|j                            | j                  \  | _        }d| j         d| j         d}| j         d| d| _        t'          j        | j                  | _         t                              d           dS )z'Initialize credentials and HTTP client.NADCservice_accountzAReranker: initializing Google Discovery Engine provider (project=z, model=z, auth=rj   r   )r  zXgoogle-auth is required for GoogleCrossEncoder. Install it with: pip install google-auth)scopesz	projects/z/locations/z&/rankingConfigs/default_ranking_configr   z:rankr   z6Reranker: Google Discovery Engine provider initialized)r+  r  rm   rn   r  r   google.oauth2r  rl   Credentialsfrom_service_account_fileSCOPESr  google.authr  defaultr  API_BASEr  r   r4  r   )r4   auth_methodr  r  r   ranking_configs         r5   r9   zGoogleCrossEncoder.initialize  s     <#F#'#;ReeARSS S15S SDOS S S	
 	
 	
 # 	K9999999   !n   !0 ; U U({ !V ! !D
"""""   !n   $*;#6#6dk#6#J#J DqvT_vvvvv MAANAAA|DL999LMMMMMs   A A.B# #B=r:   c                 F   |sg S i }t          |          D ].\  }\  }}||vrg ||<   ||                             ||f           /dgt          |          z  }|                                D ]3\  }}d |D             }d |D             }	t	          dt          |          | j                  D ]}
||
|
| j        z            }|	|
|
| j        z            }d t          |          D             }| j                            | j        | 	                                | j
        ||t          |          d          }|                                 |                                }|                    dg           D ](}t          |d	                   }|d
         |||         <   )5|S )z!Synchronous predict via REST API.r   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z4GoogleCrossEncoder._predict_sync.<locals>.<listcomp>  r   r7   c                     g | ]\  }}|S r2   r2   r   s      r5   r   z4GoogleCrossEncoder._predict_sync.<locals>.<listcomp>  r   r7   r   c                 8    g | ]\  }}t          |          |d S ))rw  content)rD   )r   r   r   s      r5   r   z4GoogleCrossEncoder._predict_sync.<locals>.<listcomp>  s*    aaawq$#a&&T::aaar7   )r   r   recordstopN)r  r   r  rw  r   )r   r   r   r   r   MAX_RECORDS_PER_REQUESTr+  r   r  r  r   r   r   r   r   )r4   r:   r  r   r   r   r  r  r   r  batch_startr  r  r  r   r   recordr|  s                     r5   r   z GoogleCrossEncoder._predict_sync  s     	I :<"+E"2"2 	4 	4C%L((&(U#&&T{3333USZZ'
$0$6$6$8$8 	K 	K E=77777E77777G  %QE

D4PQQ K K#K+@\2\$\] 'kDD`6`(` aaa)T_J`J`aaa<,,N 2244!%!&#* #G	  - 	 	 ))+++!$jjB77 K KF #F4L 1 1I;A'?J}Y788K'K. r7   c                    K   | j         t          d          |sg S t          j                    }|                    d| j        |           d{V S )z
        Score query-document pairs using Google Discovery Engine Ranking API.

        Args:
            pairs: List of (query, document) tuples to score

        Returns:
            List of relevance scores (0-1, higher = more relevant)
        Nr   )r+  r   r   r   r   r   r   s      r5   r=   zGoogleCrossEncoder.predict  sg       <STTT 	I%''))$0BEJJJJJJJJJr7   r>   )r?   r@   rA   rB   r  r  r  r   rD   rG   rV   rC   r6   dictr  r9   rE   rF   r   r=   r2   r7   r5   r  r  c  se         ":H>?F
 3*. * ** * !4Z	*
 * * * * *8 s    XF4S> F F F F"N "N "N "NH*4c3h#8 *T%[ * * * *XK4c3h#8 KT%[ K K K K K Kr7   r  c            	         ddl m}   |             }|j                                        }|dk    rJ|j        }|st          t           dt           d          t          ||j	        |j
        |j                  S |dk    r9t          |j        |j        |j        |j        |j        |j        |j                  S |d	k    rD|j        }|st          t*           dt           d
          t-          ||j        |j                  S |dk    r8|j        }|st          dt           d          t-          ||j        d          S |dk    rt6          j                            t<          t>                    }t6          j                            t@          tB                    }t6          j                            tD          tG          tH                                                              dv }tK          |||          S |dk    r'tM          |j'        |j(        |j)        |j*                  S |dk    rJ|j+        }|st          tX           dt           d          t[          ||j.        |j/        |j*                  S |dk    r>|j0        }|st          tb           dt           d          te          ||j3                  S |dk    rD|j4        }|st          tj           dt           d          tm          ||j7        |j8                  S |dk    rD|j9        }|st          tt           dt           d          tw          ||j<        |j=                   S |d!k    rt}                      S |d"k    rt                      S t          d#| d$          )%z
    Create a CrossEncoderModel instance based on configuration.

    Reads configuration via get_config() to ensure consistency across the codebase.

    Returns:
        Configured CrossEncoderModel instance
    r   )
get_configr   z is required when z	 is 'tei')r   r   rS   rN   rX   )rM   rN   rO   rP   rQ   rR   rS   r/  z is 'cohere')r  r   r   
openrouterz{HINDSIGHT_API_RERANKER_OPENROUTER_API_KEY, HINDSIGHT_API_OPENROUTER_API_KEY, or HINDSIGHT_API_LLM_API_KEY is required when z is 'openrouter'z#https://openrouter.ai/api/v1/rerankra  )true1yes)rM   r[  r]  r  )r  r  r   r  r  z is 'litellm-sdk')r  r   r  r  rC  z is 'zeroentropy')r  r   rM  z is 'siliconflow'r  z= (or HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID) is required when z is 'google')r  r   r  rU  r  zUnknown reranker provider: z. Supported: 'local', 'tei', 'cohere', 'zeroentropy', 'siliconflow', 'google', 'flashrank', 'litellm', 'litellm-sdk', 'rrf', 'jina-mlx')@configr  reranker_providerlowerreranker_tei_url
ValueErrorr,   r'   r   reranker_tei_http_timeoutreranker_tei_batch_sizereranker_tei_max_concurrentrI   reranker_local_modelreranker_local_max_concurrentreranker_local_force_cpu reranker_local_trust_remote_codereranker_local_fp16reranker_local_bucket_batchingreranker_local_batch_sizereranker_cohere_api_keyr   r)  reranker_cohere_modelreranker_cohere_base_urlreranker_openrouter_api_keyreranker_openrouter_modelr  environr   r    r   r   r	   r   rD   r
   rY  r  reranker_litellm_api_basereranker_litellm_api_keyreranker_litellm_model#reranker_litellm_max_tokens_per_docreranker_litellm_sdk_api_keyr"   r  reranker_litellm_sdk_modelreranker_litellm_sdk_api_basereranker_zeroentropy_api_keyr-   r=  reranker_zeroentropy_modelreranker_siliconflow_api_keyr(   rJ  reranker_siliconflow_modelreranker_siliconflow_base_urlreranker_google_project_idr!   r  reranker_google_model#reranker_google_service_account_keyrR  r  )	r  r  providerr   r  r   r[  r]  r  s	            r5   create_cross_encoder_from_envr&    s    $#####Z\\F'--//H5% 	j 4hhH]hhhiii$45!=	
 
 
 	
 
W		"2!?5$E+"A7
 
 
 	
 
X		0 	t ;rrOdrrrsss!.4
 
 
 	

 
\	!	!4 	iAVi i i   "2:
 
 
 	

 
[	 	 
;=]^^JNN#CEijj	
0#6^2_2_
 

%'')* %Zghhhh	Y		"53/%I	
 
 
 	
 
]	"	"5 	3ooG\ooo   &39%I	
 
 
 	
 
]	"	"5 	3ooG\ooo   '3
 
 
 	
 
]	"	"5 	3ooG\ooo   '39
 
 
 	

 
X		6
 	1 H H$9H H H   "!. & J
 
 
 	

 
U		)+++	Z		"$$$ |(  |  |  |
 
 	
r7   )GrB   r   r   r  r{   abcr   r   concurrent.futuresr   r   r  r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r   r?   rm   r/   rI   r   r  r)  r=  rJ  rR  rY  rD   r   r  r  r  r  r  r&  r2   r7   r5   <module>r)     s      				  # # # # # # # # 1 1 1 1 1 1 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (T 
	8	$	$" " " " " " " "JR
 R
 R
 R
 R
+ R
 R
 R
jT0 T0 T0 T0 T0- T0 T0 T0nK K K K K K K K\s s s s s* s s sl'1 '1 '1 '1 '1/ '1 '1 '1T'1 '1 '1 '1 '1/ '1 '1 '1T!" !" !" !" !"!2 !" !" !"Hcf cf cf cf cf- cf cf cfL+c +s +s + + + +r r r r r+ r r rj| | | | |. | | |~oK oK oK oK oK+ oK oK oKd[K [K [K [K [K* [K [K [K|y
'8 y
 y
 y
 y
 y
 y
r7   