
    j*                         d dl Z d dlmZ d dlmZmZmZmZmZm	Z	 d dl
mZ d dlmZmZ 	 d dlmZ  G d dej&                        Zed	ed   fd
       Z G d de      Z G d de      Zy# e$ r dZY Cw xY w)    N)contextmanager)AnyIteratorListOptionalTupleUnion)MetricComputationError)base_metricscore_result)
bleu_scorec                   p     e Zd ZdZdededededeee	      dee   f fdZ
dd
Zded	ee	df   fdZ xZS )BaseBLEUa  
    Base class containing shared BLEU logic, such as handling n-grams, smoothing,
    and weights initialization. This class is not intended to be used directly.

    References:
      - BLEU: Papineni et al., "BLEU: a Method for Automatic Evaluation of Machine Translation" (ACL 2002)
        https://aclanthology.org/P02-1040/
      - NLTK BLEU documentation
        https://www.nltk.org/api/nltk.translate.bleu_score.html
      - Hugging Face Evaluate: BLEU metric overview
        https://huggingface.co/spaces/evaluate-metric/bleu

    Args:
        name: The name of the metric (e.g. "sentence_bleu_metric" or "corpus_bleu_metric").
        track: Whether to track the metric (depends on your system).
        n_grams: Up to which n-gram order to use (1 through n_grams).
        smoothing_method: One of NLTK's SmoothingFunction methods (e.g. "method0", "method1", etc.).
        weights: Optional custom weights for n-gram orders. Must sum to 1.0. If None,
                 defaults to uniform distribution across `n_grams`.
        project_name: Optional project name to track the metric in for the cases when
            there are no parent span/trace to inherit project name from.
    nametrackn_gramssmoothing_methodweightsproject_namec                 f   t         |   |||       t        t        d      || _        || _        |d|z  g|z  | _        nUt        |      |k7  rt        dt        |       d| d      t        t        |      dz
        dkD  rt        d      || _        t        j                         | _        y )	N)r   r   r   zV`nltk` library is required for BLEU score calculation. Install via `pip install nltk`.      ?zLength of weights (z) != n_grams (z).gư>zWeights must sum to 1.0)super__init__nltk_bleu_scoreImportErrorr   r   r   len
ValueErrorabssumSmoothingFunction	_smootherselfr   r   r   r   r   r   	__class__s          /Users/manta/Documents/Projects/TheRoad-I1/backend/.venv/lib/python3.12/site-packages/opik/evaluation/metrics/heuristics/bleu.pyr   zBaseBLEU.__init__&   s     	d%lK"2 
  0?'M?W4DL7|w& )#g,~gYbQ  3w<#%&- !:;;"DL(::<    returnc                 l    t        | j                  | j                  | j                  j                        S N)getattrr!   r   method0)r#   s    r%   _get_smoothing_funczBaseBLEU._get_smoothing_funcG   s%    t~~t'<'<dnn>T>TUUr&   max_len.c                     t        | j                  |      }| j                  d | }t        |      xs d}|D cg c]  }||z  	 }}t	        |      S c c}w )Nr   )minr   r   r   tuple)r#   r-   
used_orderused_weightstotalw
normalizeds          r%   _truncate_weightszBaseBLEU._truncate_weightsJ   sY    w/
||KZ0L!(S)56Aa%i
6Z   7s   A)r'   z!nltk_bleu_score.SmoothingFunction)__name__
__module____qualname____doc__strboolintr   r   floatr   r,   r   r6   __classcell__r$   s   @r%   r   r      sw    .== = 	=
 = $u+&= sm=BV! !ucz1B !r&   r   r'   c               #      K   t        j                         5  t        j                  ddt        d       d  d d d        y # 1 sw Y   y xY ww)Nignorez5The hypothesis contains 0 counts of 2-gram overlaps\.znltk\.translate\.bleu_score)messagecategorymodule)warningscatch_warningsfilterwarningsUserWarning r&   r%   _suppress_bleu_warningsrK   R   s<     		 	 	"L 2		
 	 
#	"	"s   A"A	AAAc                        e Zd ZdZ	 	 	 	 	 	 ddededededeee	      dee   f fdZ
d	ed
eeee   f   dedej                  fdZ xZS )SentenceBLEUa  
    Computes sentence-level BLEU for a single candidate string vs. one or more references.

    Example:
        >>> from opik.evaluation.metrics.heuristics.bleu import SentenceBLEU
        >>> metric = SentenceBLEU(n_grams=4, smoothing_method="method1")
        >>> result = metric.score("the cat is on the mat", "the cat is on the mat")
        >>> print(result.value)
        1.0
    r   r   r   r   r   r   c                 0    t         |   ||||||       y N)r   r   r   r   r   r   r   r   r"   s          r%   r   zSentenceBLEU.__init__j   )     	-% 	 	
r&   output	referenceignored_kwargsr'   c           	         |j                         st        d      t        |t              r;|j                         st        d      |j	                         j                         g}nQg }|D ]J  }|j                         st        d      |j                  |j	                         j                                L |j	                         j                         }| j                  t        |            }| j                         }	 t               5  t        j                  ||||      }	ddd       t        j                  	| j                   d| j"                   d|	d	
      S # 1 sw Y   <xY w# t        $ r d}	Y Lw xY w)aR  
        Calculate sentence-level BLEU for one candidate vs. one or more references.

        Args:
            output: A single candidate string.
            reference: Either a single reference string or a list of reference strings.

        Returns:
            A `ScoreResult` with:
              - `value`: The sentence-level BLEU score (float).
              - `name`: The metric name.
              - `reason`: A short explanation (e.g. "Sentence-level BLEU...").

        Raises:
            MetricComputationError:
                - If the candidate or any reference is empty.
        z*Candidate is empty (single-sentence BLEU).z*Reference is empty (single-sentence BLEU).z3Encountered empty reference (single-sentence BLEU).r   smoothing_functionN        z"Sentence-level BLEU (nltk, method=): .4fvaluer   reason)stripr
   
isinstancer;   lowersplitappendr6   r   r,   rK   r   sentence_bleuZeroDivisionErrorr   ScoreResultr   r   )
r#   rR   rS   rT   	ref_listsref_strcandidate_tokensr2   smoothing_funcbleu_vals
             r%   scorezSentenceBLEU.score|   s`   . ||~()UVV i%??$,@  #*0023I I$}}0M    !6!6!89 % "<<>//1--c2B.CD113		(**88$('5	 + ''4T5J5J4K3xX[n]	
 	
 +* ! 	H	s*   
E, E %E,  E)%E, ,E:9E:)sentence_bleu_metricT   method1NNr7   r8   r9   r:   r;   r<   r=   r   r   r>   r   r	   r   r   re   rk   r?   r@   s   @r%   rM   rM   ^   s    	 + ))-&*

 
 	

 
 $u+&
 sm
$@
@
 d3i(@
 	@

 
	!	!@
r&   rM   c                        e Zd ZdZ	 	 	 	 	 	 ddededededeee	      dee   f fdZ
d	ee   d
eeeee   f      dedej                  fdZ xZS )
CorpusBLEUa  
    Computes corpus-level BLEU for multiple candidate strings vs. matching references.

    Each element in `output` corresponds to one candidate. The parallel `reference`
    element can be either a single string or a list of reference strings for that candidate.

    Example:
        >>> from opik.evaluation.metrics.heuristics.bleu import CorpusBLEU
        >>> metric = CorpusBLEU(n_grams=4, smoothing_method="method1")
        >>> outputs = ["the cat is on the mat", "there is a cat here"]
        >>> references = [
        ...     "the cat is on the mat",
        ...     ["there is a cat here", "there is cat here"]
        ... ]
        >>> result = metric.score(outputs, references)
        >>> print(result.value)
    r   r   r   r   r   r   c                 0    t         |   ||||||       y rO   rP   r"   s          r%   r   zCorpusBLEU.__init__   rQ   r&   rR   rS   rT   r'   c           	         t        |      t        |      k7  rt        d      g }g }t        ||      D ]  \  }}|j                         st        d      |j	                         j                         }t        |t              r;|j                         st        d      |j	                         j                         g}	nQg }	|D ]J  }
|
j                         st        d      |	j                  |
j	                         j                                L |j                  |       |j                  |	        t        d |D              }| j                  |      }| j                         }	 t               5  t        j                  ||||      }ddd       t!        j"                  | j$                  d	| j&                   d
|d      S # 1 sw Y   <xY w# t        $ r d}Y Lw xY w)a  
        Calculate corpus-level BLEU for multiple candidates.

        Args:
            output: A list of candidate strings (one per sample).
            reference: A list of references, each parallel to `output`. Each reference
                       item can be a single string or a list of strings.

        Returns:
            A `ScoreResult` with:
              - `value`: The corpus-level BLEU score (float).
              - `name`: The metric name.
              - `reason`: A short explanation (e.g. "Corpus-level BLEU...").

        Raises:
            MetricComputationError:
                - If a candidate or reference is empty.
                - If the number of candidates does not match the number of references.
        zEMismatch: number of candidates != number of references (corpus BLEU).z!Candidate is empty (corpus BLEU).z!Reference is empty (corpus BLEU).z*Encountered empty reference (corpus BLEU).c              3   2   K   | ]  }t        |        y wr)   )r   ).0cands     r%   	<genexpr>z#CorpusBLEU.score.<locals>.<genexpr>  s     EndD	ns   rV   NrX   z Corpus-level BLEU (nltk, method=rY   rZ   r[   )r   r
   zipr^   r`   ra   r_   r;   rb   maxr6   r,   rK   r   corpus_bleurd   r   re   r   r   )r#   rR   rS   rT   all_candidatesall_referencescandidate_strref_itemrh   rf   r_linemax_candidate_lenr2   ri   rj   s                  r%   rk   zCorpusBLEU.score   s   2 v;#i.((W  +-02'*69'=#M8 &&(,-PQQ,224::<(C(~~'01TUU%^^-3356	 	&F!<<>4H  $$V\\^%9%9%;< ' !!"23!!),- (>0  EnEE--.?@113		(**66""('5	 + ''243H3H2IXVYN[	
 	
 +* ! 	H	s*   &
G 0G
G G
G GG)corpus_bleu_metricTrm   rn   NNro   r@   s   @r%   rq   rq      s    ( ) ))-&*

 
 	

 
 $u+&
 sm
$N
S	N
 c49n-.N
 	N

 
	!	!N
r&   rq   )rF   
contextlibr   typingr   r   r   r   r   r	   opik.exceptionsr
   opik.evaluation.metricsr   r   nltk.translater   r   r   
BaseMetricr   rK   rM   rq   rJ   r&   r%   <module>r      s     % > > 2 =<
A!{%% A!H $  ^
8 ^
Bs
 s
k  Os   A* *A43A4