
    $j)                         U d Z ddlZddlmZmZ ddlmZmZ ej        ZdZ	e
ed<   dZe
ed<   d	Ze
ed
<   e	eedfdee         dede
de
de
deddfdZ G d d          ZdS )z4
Cross-encoder neural reranking for search results.
    N)datetimetimezone   )MergedCandidateScoredResultg?_RECENCY_ALPHA_TEMPORAL_ALPHA皙?_PROOF_COUNT_ALPHAFscored_resultsnowrecency_alphatemporal_alphaproof_count_alphais_passthrough_rerankerreturnc                    |j         |                    t                    }|r\| rZt          |           }t	          | d d          }t          d|dz
            }t          |          D ]\  }	}
dd|	z  |z  z
  |
_        | D ]F}
d	|
_        |
j	        j
        rq|
j	        j
        }|j         |                    t                    }||z
                                  d
z  }t          dt          dd|dz  z
                      |
_        |
j	        j        |
j	        j        nd	|
_        |
j	        j        }|=|dk    r7t          dt          dd	t!          j        |          dz  z                       }nd	}d|
_        d||
j        d	z
  z  z   }d||
j        d	z
  z  z   }d||d	z
  z  z   }|
j        |z  |z  |z  |
_        |
j        |
_        HdS )u  Apply combined scoring to a list of ScoredResults in-place.

    Uses the cross-encoder score as the primary relevance signal, with recency,
    temporal proximity, and proof count applied as multiplicative boosts. This
    ensures the influence of these secondary signals is always proportional to
    the base relevance score, regardless of the cross-encoder model's score
    calibration.

    Formula::

        recency_boost     = 1 + recency_alpha     * (recency     - 0.5)   # in [1-α/2, 1+α/2]
        temporal_boost    = 1 + temporal_alpha    * (temporal    - 0.5)   # in [1-α/2, 1+α/2]
        proof_count_boost = 1 + proof_count_alpha * (proof_norm  - 0.5)   # in [1-α/2, 1+α/2]
        combined_score    = CE_normalized * recency_boost * temporal_boost * proof_count_boost

    proof_norm maps proof_count using a smooth logarithmic curve centered at 0.5,
    clamped to [0, 1]:
      proof_count=1 → 0.5 + 0 = 0.5 (neutral multiplier)
      proof_count=150 → clamped to 1.0 (max +5% boost)

    Temporal proximity is treated as neutral (0.5) when not set by temporal retrieval,
    so temporal_boost collapses to 1.0 for non-temporal queries.

    Proof count is treated as neutral (0.5) when not available (non-observation facts),
    so proof_count_boost collapses to 1.0 for world/experience/opinion facts.

    Args:
        scored_results: Results from the cross-encoder reranker. Mutated in place.
        now: Current UTC datetime for recency calculation.
        recency_alpha: Max relative recency adjustment (default 0.2 → ±10%).
        temporal_alpha: Max relative temporal adjustment (default 0.2 → ±10%).
        proof_count_alpha: Max relative proof count adjustment (default 0.1 → ±5%).
    N)tzinfoc                 B    t          t          | dd           dd          S )N	candidate	rrf_score        )getattr)ss    m/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight_api/engine/search/reranking.py<lambda>z(apply_combined_scoring.<locals>.<lambda>[   s    ''![$"?"?cRR     Tkeyreverser   g      ?g?g      ?iQ r
   im  r   g      $@)r   replaceUTClensortedmax	enumeratecross_encoder_score_normalizedrecency	retrievaloccurred_starttotal_secondsmintemporal_proximitytemporalproof_countmathlogrrf_normalizedcombined_scoreweight)r   r   r   r   r   r   nsorted_by_rrfdenomnew_ranksroccurreddays_agor/   
proof_normrecency_boosttemporal_boostproof_count_boosts                     r   apply_combined_scoringr@      s#   R zkkk%%2  O> ORR
 
 

 Aq1u%m44 	O 	OLHb 14sX~7M0NB-- & &
<& 	D|2H&#++3+77h5577%?HS#c3(S.+A"B"BCCBJ :<9X9dbl55jm l."{a'7'7S#c3$(;2G2G$2N+O"P"PQQJJ J  mrzC/?@@~s1BCC"3zC7G"HH=MP^^arr%		=& &r   c                   L    e Zd ZdZd	dZd Zdedee         dee	         fdZ
dS )
CrossEncoderRerankerz
    Neural reranking using a cross-encoder model.

    Configured via environment variables (see cross_encoder.py).
    Default local model is cross-encoder/ms-marco-MiniLM-L-6-v2.
    Nc                 F    |ddl m}  |            }|| _        d| _        dS )z
        Initialize cross-encoder reranker.

        Args:
            cross_encoder: CrossEncoderModel instance. If None, creates one from
                          environment variables (defaults to local provider)
        Nr   )create_cross_encoder_from_envF)"hindsight_api.engine.cross_encoderrD   cross_encoder_initialized)selfrF   rD   s      r   __init__zCrossEncoderReranker.__init__   s@      XXXXXX99;;M*!r   c                    K   | j         rdS ddl| j        j        dk    r5                                }|                    dfd           d{V  n                                 d{V  d| _         dS )zHEnsure the cross-encoder model is initialized (for lazy initialization).Nr   localc                  R                                                                    S N)run
initialize)asynciorF   s   r   r   z9CrossEncoderReranker.ensure_initialized.<locals>.<lambda>   s    W[[AYAYA[A[5\5\ r   T)rG   rP   rF   provider_nameget_event_looprun_in_executorrO   )rH   looprP   rF   s     @@r   ensure_initializedz'CrossEncoderReranker.ensure_initialized   s       	F*&'11))++D&&t-\-\-\-\-\]]]]]]]]]]**,,,,,,,,, r   query
candidatesr   c                   K   |sg S g }|D ]}}|j         }|j        }|j        r|j         d| }|j        r<|j        }|                    d          }|                    d          }	d|	 d| d| }|                    ||g           ~| j                            |           d{V }
ddl}ddl	fd	fd
|
D             }g }t          ||
|          D ]p\  }}}t          |          }t          |          } |j        |          rd} |j        |          rd}t          ||||          }|                    |           q|                    d d           |S )z
        Rerank candidates using cross-encoder scores.

        Args:
            query: Search query
            candidates: Merged candidates from RRF

        Returns:
            List of ScoredResult objects sorted by cross-encoder score
        z: z%Y-%m-%dz	%B %d, %Yz[Date: z (z)] Nr   c                 <    dd                     |            z   z  S )Nr   )exp)xnps    r   sigmoidz,CrossEncoderReranker.rerank.<locals>.sigmoid   s    BFFA2JJ''r   c                 &    g | ]} |          S  r_   ).0scorer]   s     r   
<listcomp>z/CrossEncoderReranker.rerank.<locals>.<listcomp>   s!    @@@WWU^^@@@r   r   )r   cross_encoder_scorer'   r4   c                     | j         S rM   )r4   )r[   s    r   r   z-CrossEncoderReranker.rerank.<locals>.<lambda>   s    !( r   Tr   )r)   textcontextr*   strftimeappendrF   predictr0   numpyzipfloatisnanr   sort)rH   rV   rW   pairsr   r)   doc_textr*   date_isodate_readablescoresr0   normalized_scoresr   	raw_score
norm_scorerawnormscored_resultr\   r]   s                      @@r   rerankzCrossEncoderReranker.rerank   s$       	I # 	, 	,I!+I !~H  >'/==8== ' N!*!9 *22:>> !/ 7 7 D D N]MMhMM8MMLL%*++++ )11%88888888 		( 	( 	( 	( 	( A@@@@@@ 03JHY0Z0Z 	1 	1,Iy* 	""C$$Dtz# tz$ (#$'/3	  M !!-0000 	 2 2DAAAr   rM   )__name__
__module____qualname____doc__rI   rU   strlistr   r   rz   r_   r   r   rB   rB      s~         " " " "! ! ! M# M43H MTR^M_ M M M M M Mr   rB   )r~   r0   r   r   typesr   r   utcr"   r   rl   __annotations__r	   r   r   boolr@   rB   r_   r   r   <module>r      s@      ' ' ' ' ' ' ' ' 0 0 0 0 0 0 0 0l
        E    *+1$)n& n&&n&	n& n& 	n&
 n& "n& 
n& n& n& n&bt t t t t t t t t tr   