U
    DAf-                  	   @   s   d dl mZ d dlmZmZmZmZmZ d dlm	Z	m
Z
mZmZmZ d dlmZ d dlmZmZmZmZ d dlmZmZmZ eeZedd&Zd d	lZd d	lZd d
lm Z m!Z! W 5 Q R X e
G dd dZ"d	S )    )Path)AnyDictListOptionalUnion)Document	componentdefault_from_dictdefault_to_dictlogging)
LazyImport)ComponentDevice	DeviceMapSecretdeserialize_secrets_inplace)deserialize_hf_model_kwargsresolve_hf_device_mapserialize_hf_model_kwargsz3Run 'pip install transformers[torch,sentencepiece]')messageN)"AutoModelForSequenceClassificationAutoTokenizerc                   @   s  e Zd ZdZddejddgdddd	d	dd
ddddfeeef e	e
 e	e eeee	ee  eee	e e	e e	eeef  dddZeeef dddZdd Zeeef dddZeeeef d dddZejee ddeee e	e e	e e	e e	e dddZdS )TransformersSimilarityRankeraY  
    Ranks Documents based on their similarity to the query.

    It uses a pre-trained cross-encoder model (from the Hugging Face Hub) to embed the query and the Documents.

    Usage example:
    ```python
    from haystack import Document
    from haystack.components.rankers import TransformersSimilarityRanker

    ranker = TransformersSimilarityRanker()
    docs = [Document(content="Paris"), Document(content="Berlin")]
    query = "City in Germany"
    ranker.warm_up()
    result = ranker.run(query=query, documents=docs)
    docs = result["documents"]
    print(docs[0].content)
    ```
    z$cross-encoder/ms-marco-MiniLM-L-6-v2NZHF_API_TOKENZHF_TOKENF)strict
    
Tg      ?)modeldevicetokentop_kquery_prefixdocument_prefixmeta_fields_to_embedembedding_separatorscale_scorecalibration_factorscore_thresholdmodel_kwargsc                 C   s   t   t|| _d| _|| _|| _d| _d| _|| _	|| _
|pBg | _|| _|	| _|
| _|| _t||d}|| _| jr| jdkrtd|
 | j	dkrtd| dS )ac  
        Creates an instance of TransformersSimilarityRanker.

        :param model:
            The name or path of a pre-trained cross-encoder model from the Hugging Face Hub.
        :param device:
            The device on which the model is loaded. If `None`, the default device is automatically selected.
        :param token:
            The API token used to download private models from Hugging Face.
        :param top_k:
            The maximum number of Documents to return per query.
        :param query_prefix:
            A string to add to the beginning of the query text before ranking.
            Can be used to prepend the text with an instruction, as required by some reranking models, such as bge.
        :param document_prefix:
            A string to add to the beginning of each Document text before ranking. Can be used to prepend the text with
            an instruction, as required by some embedding models, such as bge.
        :param meta_fields_to_embed:
            List of meta fields that should be embedded along with the Document content.
        :param embedding_separator:
            Separator used to concatenate the meta fields to the Document content.
        :param scale_score:
            Whether the raw logit predictions will be scaled using a Sigmoid activation function.
            Set this to False if you do not want any scaling of the raw logit predictions.
        :param calibration_factor:
            Factor used for calibrating probabilities calculated by `sigmoid(logits * calibration_factor)`.
            This is only used if `scale_score` is set to True.
        :param score_threshold:
            If provided only returns documents with a score above this threshold.
        :param model_kwargs: Additional keyword arguments passed to `AutoModelForSequenceClassification.from_pretrained`
            when loading the model specified in `model`. For details on what kwargs you can pass,
            see the model's documentation.

        :raises ValueError:
            If `top_k` is not > 0.
            If `scale_score` is True and `calibration_factor` is not provided.
        N)r   r(   Dscale_score is True so calibration_factor must be provided, but got r   top_k must be > 0, but got )torch_and_transformers_importcheckstrmodel_name_or_pathr   r!   r"   	tokenizerr   r    r   r#   r$   r%   r&   r'   r   r(   
ValueError)selfr   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(    r2   W/tmp/pip-unpacked-wheel-z752163x/haystack/components/rankers/transformers_similarity.py__init__,   s,    4


z%TransformersSimilarityRanker.__init__)returnc                 C   s
   d| j iS )zC
        Data that is sent to Posthog for usage analytics.
        r   )r.   r1   r2   r2   r3   _get_telemetry_data|   s    z0TransformersSimilarityRanker._get_telemetry_datac                 C   st   | j dkrptj| jfd| jr&| j ndi| j| _ tj| j| jrN| j ndd| _t	j
t| j jd| _dS )z,
        Initializes the component.
        Nr   )r   )Z
device_map)r   r   Zfrom_pretrainedr.   r   Zresolve_valuer(   r   r/   r   Zfrom_multipler   Zfrom_hfZhf_device_mapr   r6   r2   r2   r3   warm_up   s    
 z$TransformersSimilarityRanker.warm_upc                 C   sZ   t | d| j| jr| j nd| j| j| j| j| j| j	| j
| j| jd}t|d d  |S )z{
        Serializes the component to a dictionary.

        :returns:
            Dictionary with serialized data.
        N)r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   init_parametersr(   )r   r.   r   to_dictr    r!   r"   r#   r$   r%   r&   r'   r(   r   )r1   Zserialization_dictr2   r2   r3   r:      s"    z$TransformersSimilarityRanker.to_dict)datar5   c                 C   sZ   |d }| ddk	r(t|d |d< | ddk	rBt|d  t|dgd t| |S )z
        Deserializes the component from a dictionary.

        :param data:
            Dictionary to deserialize from.
        :returns:
            Deserialized component.
        r9   r   Nr(   r   )keys)getr   	from_dictr   r   r
   )clsr;   Zinit_paramsr2   r2   r3   r>      s    
z&TransformersSimilarityRanker.from_dict)	documents)queryr@   r    r%   r&   r'   c              	      s  | j dkrtd|sdg iS |p&| j}|p0| j}|p:| j}pD| j|dkr\td| |rv|dkrvtd| g }|D ]J  fdd| jD }| j	| j
pd	g }	|| j| | j|	 g q~| j|d
d
dd| jj }
t  | j f |
jjdd}W 5 Q R X |r(t|| }tj|d
d\}}|  }|  }g }|D ]&}|}|| || _|||  qZdk	rfdd|D }d|d| iS )a  
        Returns a list of Documents ranked by their similarity to the given query.

        :param query:
            Query string.
        :param documents:
            List of Documents.
        :param top_k:
            The maximum number of Documents you want the Ranker to return.
        :param scale_score:
            Whether the raw logit predictions will be scaled using a Sigmoid activation function.
            Set this to False if you do not want any scaling of the raw logit predictions.
        :param calibration_factor:
            Factor used for calibrating probabilities calculated by
            `sigmoid(logits * calibration_factor)`. This is only used if `scale_score` is set to True.
        :param score_threshold:
            If provided only returns documents with a score above this threshold.
        :returns:
            A dictionary with the following keys:
            - `documents`: List of Documents most similar to the given query in descending order of similarity.

        :raises ValueError:
            If `top_k` is not > 0.
            If `scale_score` is True and `calibration_factor` is not provided.
        :raises RuntimeError:
            If the model is not loaded because `warm_up()` was not called before.
        NzdThe component TransformersSimilarityRanker wasn't warmed up. Run 'warm_up()' before calling 'run()'.r@   r   r*   r)   c                    s.   g | ]&}| j kr j | rt j | qS r2   )metar-   ).0key)docr2   r3   
<listcomp>   s    
 
 z4TransformersSimilarityRanker.run.<locals>.<listcomp>r   Tpt)paddingZ
truncationZreturn_tensors   )Zdim)Z
descendingc                    s   g | ]}|j  kr|qS r2   )score)rC   rE   )r'   r2   r3   rF     s     
 )r   RuntimeErrorr    r%   r&   r'   r0   r#   r$   joincontentappendr!   r"   r/   tor   Zfirst_deviceZto_torchtorchZinference_modeZlogitsZsqueezeZsigmoidsortcputolistrJ   )r1   rA   r@   r    r%   r&   r'   Zquery_doc_pairsZmeta_values_to_embedZtext_to_embedfeaturesZsimilarity_scores_Zsorted_indicesZranked_docsZsorted_indexir2   )rE   r'   r3   run   sR    &







 
z TransformersSimilarityRanker.run)NNNN)__name__
__module____qualname____doc__r   Zfrom_env_varr   r-   r   r   r   intr   boolfloatr   r   r4   r7   r8   r:   classmethodr>   r	   Zoutput_typesr   rW   r2   r2   r2   r3   r      sZ   

P    r   )#pathlibr   typingr   r   r   r   r   Zhaystackr   r	   r
   r   r   Zhaystack.lazy_importsr   Zhaystack.utilsr   r   r   r   Zhaystack.utils.hfr   r   r   	getLoggerrX   loggerr+   Z
acceleraterP   Ztransformersr   r   r   r2   r2   r2   r3   <module>   s   
