o
    +i:                     @  s   d Z ddlmZ ddlZddlZddlmZmZmZm	Z	m
Z
mZmZ ddlmZ ddlmZ ddlmZ ddlmZ erHdd	lmZ dd
lmZ G dd dZe ZG dd dZG dd dZe ZdgZdS )a<  Make approximate assertions as "expectations" on test results.

This module is designed to be used within test cases decorated with the
`@pytest.mark.decorator` decorator

It allows you to log scores about a test case and optionally make assertions that log as
"expectation" feedback to LangSmith.

Example:
    ```python
    import pytest
    from langsmith import expect


    @pytest.mark.langsmith
    def test_output_semantically_close():
        response = oai_client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "You are a helpful assistant."},
                {"role": "user", "content": "Say hello!"},
            ],
        )
        response_txt = response.choices[0].message.content
        # Intended usage
        expect.embedding_distance(
            prediction=response_txt,
            reference="Hello!",
        ).to_be_less_than(0.9)

        # Score the test case
        matcher = expect.edit_distance(
            prediction=response_txt,
            reference="Hello!",
        )
        # Apply an assertion and log 'expectation' feedback to LangSmith
        matcher.to_be_less_than(1)

        # You can also directly make assertions on values directly
        expect.value(response_txt).to_contain("Hello!")
        # Or using a custom check
        expect.value(response_txt).against(lambda x: "Hello" in x)

        # You can even use this for basic metric logging within tests

        expect.score(0.8)
        expect.score(0.7, key="similarity").to_be_greater_than(0.7)
    ```
    )annotationsN)TYPE_CHECKINGAnyCallableLiteralOptionalUnionoverloadclient)run_helpers)	run_trees)utils)EditDistanceConfig)EmbeddingConfigc                   @  s$   e Zd ZdZd
ddZdddZd	S )_NULL_SENTRYzA sentinel singleton class used to distinguish omitted keyword arguments
    from those passed in with the value None (which may have different behavior).
    returnLiteral[False]c                 C     dS )NF selfr   r   N/var/www/html/psymed-ai/venv/lib/python3.10/site-packages/langsmith/_expect.py__bool__Q      z_NULL_SENTRY.__bool__strc                 C  r   )N	NOT_GIVENr   r   r   r   r   __repr__T   r   z_NULL_SENTRY.__repr__N)r   r   )r   r   )__name__
__module____qualname____doc__r   r   r   r   r   r   r   L   s    
r   c                   @  s   e Zd ZdZ		d2d3ddZd4d5ddZd6ddZd7ddZd7ddZd8d"d#Z	d9d:d&d'Z
d7d(d)Zd;d*d+Zd<d,d-Zd=d0d1ZdS )>_Matcherz4A class for making assertions on expectation values.Nr   Optional[ls_client.Client]keyr   valuer   	_executor,Optional[ls_utils.ContextThreadPoolExecutor]run_idOptional[str]c                 C  sF   || _ || _|| _|ptjdd| _t }|r|j| _	d S || _	d S )N   max_workers)
_clientr$   r%   ls_utilsContextThreadPoolExecutorr&   rhget_current_run_treetrace_id_run_id)r   r   r$   r%   r&   r(   rtr   r   r   __init__^   s   z_Matcher.__init__scoreintmessager   Nonec                 C  s<   t  s| jst | _| jj| jj| jd||d d S d S )Nexpectation)r(   r$   r6   comment)	r.   test_tracking_is_disabledr-   r4   get_cached_clientr&   submitcreate_feedbackr3   )r   r6   r8   r   r   r   _submit_feedbackm   s   

z_Matcher._submit_feedback	conditionboolmethod_namec              
   C  s^   z|sJ || j dd| j d| d W d S  ty. } z|  dt| |d d }~ww )N   z	Success: .)r8   r   )r@   r$   AssertionErrorrepr)r   rA   r8   rC   er   r   r   _asserty   s   "z_Matcher._assertfloatc              	   C  s.   |  | j|k d| j d| d| j d dS )zAssert that the expectation value is less than the given value.

        Args:
            value: The value to compare against.

        Raises:
            AssertionError: If the expectation value is not less than the given value.
        	Expected z to be less than 
, but got to_be_less_thanNrI   r%   r$   r   r%   r   r   r   rM      s
   	z_Matcher.to_be_less_thanc              	   C  s.   |  | j|kd| j d| d| j d dS )a  Assert that the expectation value is greater than the given value.

        Args:
            value: The value to compare against.

        Raises:
            AssertionError: If the expectation value is not
            greater than the given value.
        rK   z to be greater than rL   to_be_greater_thanNrN   rO   r   r   r   rP      
   
z_Matcher.to_be_greater_than	min_value	max_valuec                 C  sD   |  || j  k o|k n  d| j d| d| d| j d dS )a4  Assert that the expectation value is between the given min and max values.

        Args:
            min_value: The minimum value (exclusive).
            max_value: The maximum value (exclusive).

        Raises:
            AssertionError: If the expectation value is not between the min and max.
        rK   z to be between z and rL   to_be_betweenNrN   )r   rR   rS   r   r   r   rT      s   
z_Matcher.to_be_between   	precisionc              	   C  s:   |  t| j|t||kd| j d| d| j d dS )ak  Assert that the expectation value is approximately equal to the given value.

        Args:
            value: The value to compare against.
            precision: The number of decimal places to round to for comparison.

        Raises:
            AssertionError: If the rounded expectation value
                does not equal the rounded given value.
        rK   z to be approximately rL   to_be_approximatelyN)rI   roundr%   r$   )r   r%   rV   r   r   r   rW      s
   z_Matcher.to_be_approximatelyc              	   C  s.   |  | j|kd| j d| d| j d dS )a   Assert that the expectation value equals the given value.

        Args:
            value: The value to compare against.

        Raises:
            AssertionError: If the expectation value does
                not exactly equal the given value.
        rK   z to be equal to rL   to_equalNrN   rO   r   r   r   rY      rQ   z_Matcher.to_equalc                 C  s(   |  | jdu d| j d| j d dS )zAssert that the expectation value is `None`.

        Raises:
            AssertionError: If the expectation value is not `None`.
        NrK   z to be None, but got 
to_be_nonerN   r   r   r   r   rZ      s
   z_Matcher.to_be_nonec                 C  s(   |  || jv d| j d| dd dS )zAssert that the expectation value contains the given value.

        Args:
            value: The value to check for containment.

        Raises:
            AssertionError: If the expectation value does not contain the given value.
        rK   z to contain z, but it does not
to_containNrN   rO   r   r   r   r[      s
   	z_Matcher.to_containfuncr   c                C  s0   t |}| || jd| d| j d dS )zAssert the expectation value against a custom function.

        Args:
            func: A custom function that takes the expectation value as input.

        Raises:
            AssertionError: If the custom function returns False.
        z
Assertion z failed for againstN)inspect	signaturerI   r%   r$   )r   r\   func_signaturer   r   r   r]      s   
	z_Matcher.against)NN)
r   r#   r$   r   r%   r   r&   r'   r(   r)   N)r6   r7   r8   r)   r   r9   )rA   rB   r8   r   rC   r   r   r9   )r%   rJ   r   r9   )rR   rJ   rS   rJ   r   r9   )rU   )r%   rJ   rV   r7   r   r9   )r   r9   )r%   r   r   r9   )r\   r   r   r9   )r   r   r    r!   r5   r@   rI   rM   rP   rT   rW   rY   rZ   r[   r]   r   r   r   r   r"   [   s    






r"   c                   @  s   e Zd ZdZddd.ddZddd/ddZddd0ddZd1ddZddddd2d!d"Ze	d1d#d$Z
e	d3d&d$Z
edfd4d)d$Z
d5d,d-ZdS )6_Expectz1A class for setting expectations on test results.Nr
   r   r#   c                C  s*   || _ tjdd| _tj| jjdd d S )Nr*   r+   T)wait)r-   r.   r/   executoratexitregistershutdownr   r   r   r   r   r5     s   z_Expect.__init__config
predictionr   	referencerj   Optional[EmbeddingConfig]r   r"   c          	   	   C  s   ddl m} |p	i }|drdnd}||d}|j||d}||jd}| d	||d
| d|j d t| jd	|| jdS )a  Compute the embedding distance between the prediction and reference.

        This logs the embedding distance to LangSmith and returns a `_Matcher` instance
        for making assertions on the distance value.

        By default, this uses the OpenAI API for computing embeddings.

        Args:
            prediction: The predicted string to compare.
            reference: The reference string to compare against.
            config: Optional configuration for the embedding distance evaluator.

                Supported options:

                - `encoder`: A custom encoder function to encode the list of input
                    strings to embeddings.

                    Defaults to the OpenAI API.
                - `metric`: The distance metric to use for comparison.

                    Supported values: `'cosine'`, `'euclidean'`, `'manhattan'`,
                    `'chebyshev'`, `'hamming'`.

        Returns:
            A `_Matcher` instance for the embedding distance value.


        Example:
            ```python
            expect.embedding_distance(
                prediction="hello",
                reference="hi",
            ).to_be_less_than(1.0)
            ```
        r   )EmbeddingDistanceencodercustomopenairi   rk   rl   )ro   metricembedding_distanceUsing z
, Metric: r6   source_infor;   r&   )	'langsmith._internal._embedding_distancern   getevaluatedistancer@   r"   r-   rd   )	r   rk   rl   rj   rn   encoder_func	evaluatorr6   src_infor   r   r   rt     s    *
z_Expect.embedding_distanceOptional[EditDistanceConfig]c          
   	   C  s   ddl m} |p	i }|dpd}|dd}||d}|j||d}||d	}	| d
||	d| d| d t| jd
|| jdS )aB  Compute the string distance between the prediction and reference.

        This logs the string distance (Damerau-Levenshtein) to LangSmith and returns
        a `_Matcher` instance for making assertions on the distance value.

        This depends on the `rapidfuzz` package for string distance computation.

        Args:
            prediction: The predicted string to compare.
            reference: The reference string to compare against.
            config: Optional configuration for the string distance evaluator.

                Supported options:

                - `metric`: The distance metric to use for comparison.

                    Supported values: `'damerau_levenshtein'`, `'levenshtein'`,
                    `'jaro'`, `'jaro_winkler'`, `'hamming'`, `'indel'`.
                - `normalize_score`: Whether to normalize the score between `0` and `1`.

        Returns:
            A `_Matcher` instance for the string distance value.

        Examples:
            ```python
            expect.edit_distance("hello", "helo").to_be_less_than(1)
            ```
        r   )EditDistancers   damerau_levenshteinnormalize_scoreTri   rr   )rs   	normalizeedit_distanceru   z, Normalize: rv   rx   )"langsmith._internal._edit_distancer   rz   r{   r@   r"   r-   rd   )
r   rk   rl   rj   r   rs   r   r~   r6   r   r   r   r   r   D  s(   #

z_Expect.edit_distancer%   r   c                 C  s   t | jd|| jdS )aD  Create a `_Matcher` instance for making assertions on the given value.

        Args:
            value: The value to make assertions on.

        Returns:
            A `_Matcher` instance for the given value.

        Example:
            ```python
            expect.value(10).to_be_less_than(20)
            ```
        r%   rx   )r"   r-   rd   rO   r   r   r   r%   ~  s   z_Expect.valuer6   )r$   source_run_idr;   Union[float, int, bool]r$   r   Optional[ls_client.ID_TYPE]r;   r)   c                C  s.   |  ||ddi||d t| j||| jdS )a  Log a numeric score to LangSmith.

        Args:
            score: The score value to log.
            key: The key to use for logging the score. Defaults to `'score'`.

        Example:
            ```python
            expect.score(0.8)  # doctest: +ELLIPSIS
            <langsmith._expect._Matcher object at ...>

            expect.score(0.8, key="similarity").to_be_greater_than(0.7)
            ```
        methodzexpect.score)r6   rw   r   r;   rx   )r@   r"   r-   rd   )r   r6   r$   r   r;   r   r   r   r6     s   	z_Expect.scorec                C     d S ra   r   rO   r   r   r   __call__  r   z_Expect.__call__ls_client.Clientc               C  r   ra   r   rh   r   r   r   r     r   Optional[Any]Union[_Expect, _Matcher]c                C  s    t |d}|tur||S |S )Nr
   )rb   r   r%   )r   r%   r   expectedr   r   r   r     s   

resultsdictc                 C  sT   t  }|r	|jnd }t s(| jst | _| jj	| jj
f||d| d S d S )N)r(   r$   )r0   r1   r2   r.   r<   r-   r4   r=   rd   r>   r?   )r   r$   r   current_runr(   r   r   r   r@     s   

z_Expect._submit_feedback)r   r#   )rk   r   rl   r   rj   rm   r   r"   )rk   r   rl   r   rj   r   r   r"   )r%   r   r   r"   )
r6   r   r$   r   r   r   r;   r)   r   r"   )r   r   r   rb   )r   r#   r%   r   r   r   )r$   r   r   r   )r   r   r    r!   r5   rt   r   r%   r6   r	   r   r   r@   r   r   r   r   rb      s(    
B
:#rb   expect) r!   
__future__r   re   r^   typingr   r   r   r   r   r   r	   	langsmithr   	ls_clientr   r0   r   r4   r   r.   r   r   ry   r   r   r   r"   rb   r   __all__r   r   r   r   <module>   s(    2$
 % O
