o
    i&                     @   s   d Z ddlZddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlm	Z	 dd	lm
Z
 G d
d deZG dd deZdS )z&String matching evaluators for LLMObs.    N)Any)Callable)Optional)Pattern)BaseEvaluator)EvaluatorContext)EvaluatorResultc                       s   e Zd ZdZdZ						ddeded	ed
eee	gee f  deee	gee f  dee f fddZ
dedefddZ  ZS )StringCheckEvaluatoraP  Evaluator that performs string comparison operations.

    Compares the output_data with expected_output using various string operations.
    Supports operations similar to OpenAI's String Check Grader.

    Operations:
    - 'eq': Equals (exact match) - default
    - 'ne': Not equals (inequality)
    - 'contains': Contains substring (case-sensitive)
    - 'icontains': Contains substring (case-insensitive)

    Example::

        # Exact match (default)
        evaluator = StringCheckEvaluator(operation="eq", case_sensitive=True)
        result = evaluator.evaluate(context)
        # Returns: True if output == expected_output

        # Not equals
        evaluator = StringCheckEvaluator(operation="ne")
        # Returns: True if output != expected_output

        # Contains (case-sensitive)
        evaluator = StringCheckEvaluator(operation="contains")
        # Returns: True if expected_output is in output

        # Contains (case-insensitive)
        evaluator = StringCheckEvaluator(operation="icontains")
        # Returns: True if expected_output is in output (ignoring case)

        # Extract field from dict output
        evaluator = StringCheckEvaluator(
            operation="eq",
            output_extractor=lambda x: x.get("message", "") if isinstance(x, dict) else str(x),
        )
        # Extracts "message" field from dict before comparison

    :param operation: String comparison operation: 'eq', 'ne', 'contains', 'icontains' (default: 'eq')
    :param case_sensitive: Whether to perform case-sensitive comparison (default: True, ignored for 'icontains')
    :param strip_whitespace: Whether to strip leading/trailing whitespace before comparison (default: False)
    :param output_extractor: Optional function to extract/transform output_data before comparison.
                             Must return a str or None. (default: None)
    :param expected_output_extractor: Optional function to extract/transform expected_output before
                                      comparison. Must return a str or None. (default: None)
    :param name: Optional custom name for the evaluator
    )eqnecontains	icontainsr
   TFN	operationcase_sensitivestrip_whitespaceoutput_extractorexpected_output_extractornamec                    s   t  j|d || jvrtd| j d| |dur#t|s#td|dur/t|s/td|| _|| _|| _|| _	|| _
dS )a  Initialize the StringCheckEvaluator evaluator.

        :param operation: String comparison operation: 'eq', 'ne', 'contains', 'icontains'
        :param case_sensitive: Whether to perform case-sensitive comparison
        :param strip_whitespace: Whether to strip whitespace before comparison
        :param output_extractor: Optional function to extract/transform output_data before comparison.
                                 Must return a str or None.
        :param expected_output_extractor: Optional function to extract/transform expected_output before
                                          comparison. Must return a str or None.
        :param name: Optional custom name for the evaluator
        :raises ValueError: If operation is invalid
        r   zoperation must be one of z, got: N,output_extractor must be a callable functionz5expected_output_extractor must be a callable function)super__init__VALID_OPERATIONS
ValueErrorcallable	TypeErrorr   r   r   r   r   )selfr   r   r   r   r   r   	__class__ ^/home/ubuntu/.local/lib/python3.10/site-packages/ddtrace/llmobs/_evaluators/string_matching.pyr   @   s   

zStringCheckEvaluator.__init__contextreturnc                 C   s<  |j }|j}| jdur| |}| jdur| |}|du r3|du r3| jdk}t||r/ddS ddS |du s;|du rW| jdkrFtdddS | jdkrQtdddS tdddS t|}t|}| jrj| }| }| jd	ksr| j	sz|
 }|
 }| jdkr||k}n| jdkr||k}n||v }t||rddS ddS )
zPerform string comparison evaluation.

        :param context: The evaluation context
        :return: EvaluatorResult with boolean value and pass/fail assessment
        Nr
   passfailvalue
assessmentFr   Tr   )output_dataexpected_outputr   r   r   r   strr   stripr   lower)r   r!   outputexpectedresult
output_strexpected_strr   r   r    evaluatef   s:   










zStringCheckEvaluator.evaluate)r
   TFNNN)__name__
__module____qualname____doc__r   r*   boolr   r   r   r   r   r   r2   __classcell__r   r   r   r    r	      s.    /&r	   c                       sh   e Zd ZdZ				ddedededeeegee f  d	ee f
 fd
dZ	de
defddZ  ZS )RegexMatchEvaluatora  Evaluator that performs regex pattern matching.

    Checks if the output_data matches a given regex pattern.
    Useful for validating structured outputs like emails, phone numbers,
    URLs, or any custom format requirements.

    Example::

        # Validate email format
        evaluator = RegexMatchEvaluator(pattern=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
        result = evaluator.evaluate(context)
        # Returns: True if pattern matches, False otherwise

        # Extract field from dict output before regex matching
        evaluator = RegexMatchEvaluator(
            pattern=r"\d{3}-\d{4}",
            output_extractor=lambda x: x.get("phone", "") if isinstance(x, dict) else str(x)
        )
        # Extracts "phone" field from dict before pattern matching

    :param pattern: The regex pattern to match against
    :param match_mode: How to match - 'search' (partial), 'match' (from start), or 'fullmatch' (entire string)
    :param flags: Optional regex flags (e.g., re.IGNORECASE)
    :param output_extractor: Optional function to extract/transform output_data before matching.
                             Must return a str or None. (default: None)
    :param name: Optional custom name for the evaluator
    searchr   Npattern
match_modeflagsr   r   c              
      s   t  j|d |dvrtd| z	t||| _W n tjy/ } ztd| d}~ww |dur<t|s<td|| _	|| _
|| _|| _dS )a  Initialize the RegexMatchEvaluator evaluator.

        :param pattern: The regex pattern string
        :param match_mode: One of 'search', 'match', or 'fullmatch'
        :param flags: Regex flags (e.g., re.IGNORECASE, re.MULTILINE)
        :param output_extractor: Optional function to extract/transform output_data before matching.
                                 Must return a str or None.
        :param name: Optional custom name for the evaluator
        :raises ValueError: If match_mode is invalid or pattern is invalid
        r   )r:   match	fullmatchz;match_mode must be 'search', 'match', or 'fullmatch', got: zInvalid regex pattern: Nr   )r   r   r   recompiler;   errorr   r   pattern_strr<   r=   r   )r   r;   r<   r=   r   r   er   r   r    r      s   
zRegexMatchEvaluator.__init__r!   r"   c                 C   s   |j }| jdur| |}|du rtdddS t|}| jdkr'| j|}n| jdkr3| j|}n| j|}|du}t||rEddS ddS )zPerform regex match evaluation.

        :param context: The evaluation context
        :return: EvaluatorResult with boolean value and pass/fail assessment
        NFr$   r%   r:   r>   r#   )	r(   r   r   r*   r<   r;   r:   r>   r?   )r   r!   r-   r0   r>   r/   r   r   r    r2      s   



zRegexMatchEvaluator.evaluate)r:   r   NN)r3   r4   r5   r6   r*   intr   r   r   r   r   r   r2   r8   r   r   r   r    r9      s$    $r9   )r6   r@   typingr   r   r   r   ddtrace.llmobs._experimentr   r   r   r	   r9   r   r   r   r    <module>   s     