
    jb                        d Z ddlZddlmZmZ ddlmZmZmZm	Z	m
Z
 g dZg dZg dZg dZg dZg d	Z G d
 de      Z G d de      Z G d de      Z G d de      Z G d de      Z G d de      ZddhZ e       Z G d d      Zy)z
This file contains the OQL parser and validator. It is currently limited in scope to only support
simple filters without "or" operators.
    N)ABCabstractmethod)AnyDictOptionalTupleList)=!=containsnot_containsstarts_with	ends_with><)r
   r   r   >=r   <=)r
   r   r   r   r   r   is_emptyis_not_empty)r
   r   r   r   r   r   )
r
   r   r   r   r   r   r   r   r   r   c                       e Zd ZdZeedeeef   fd              Zeedeee	e   f   fd              Z
ede	e   fd       Zy)	OQLConfigz*Abstract base class for OQL configuration.returnc                      y)z-Return the supported columns and their types.N selfs    }/Users/manta/Documents/Projects/TheRoad-I1/backend/.venv/lib/python3.12/site-packages/opik/api_objects/opik_query_language.pycolumnszOQLConfig.columns9        	    c                      y)z/Return the supported operators for each column.Nr   r   s    r   supported_operatorszOQLConfig.supported_operators?   r   r    c                 
    g dS )z>Return fields that support nested key access via dot notation.)usagefeedback_scoresmetadatar   r   s    r   dictionary_fieldszOQLConfig.dictionary_fieldsE   s
     87r    N)__name__
__module____qualname____doc__propertyr   r   strr   r	   r"   r'   r   r    r   r   r   6   s}    4c3h    T#tCy.%9    849 8 8r    r   c                   r    e Zd ZdZedeeef   fd       Zedeeee   f   fd       Z	edee   fd       Z
y)TraceOQLConfigzOQL configuration for trace filtering.

    Based on backend's TraceField enum.
    See: apps/opik-backend/src/main/java/com/comet/opik/api/filter/TraceField.java
    r   c           
          i ddddddddddddd	d
dd
dd
dddddddddddddddddddddddddS )Nidstringname
start_time	date_timeend_timeinputoutput
input_json
dictionaryoutput_jsonr&   total_estimated_costnumberllm_span_counttagslistusage.total_tokensusage.prompt_tokensusage.completion_tokensr%   feedback_scores_numberspan_feedback_scoreserror_container)duration	thread_id
guardrails
error_info
created_atlast_updated_atannotation_queue_idsexperiment_idr   r   s    r   r   zTraceOQLConfig.columnsR   s    
(
H
 +
 	

 X
 h
 ,
 <
 
 #H
 h
 F
 !(
 "8
 &x
  7!
" #$<#
$ !!"+%*$*%3
 	
r    c                 P   i dt         dt         dt         dt         dt         dt         dt         dt        d	t        d
t        dt        dt        dt        dt        dt        dt        dt        t        t        t        t        t        t
        t
        ddgt         d	S )Nr1   r3   r7   r8   rH   rI   rN   r4   r6   rK   rL   r<   r>   rA   rB   rC   rG   r   r   )	r9   r;   r&   r%   rE   r?   rM   rJ   defaultSTRING_OPERATORSDATE_TIME_OPERATORSNUMBER_OPERATORSDICTIONARY_OPERATORSFEEDBACK_SCORES_OPERATORSLIST_OPERATORSr   s    r   r"   z"TraceOQLConfig.supported_operatorsp   s   
"
$
 %
 &	

 )
 *
 -
 -
 +
 -
 2
 #$4
 .
 !"2
 "#3
  &'7!
" (#
$ //,8$="$2%~6'5
 	
r    c                 
    g dS )N)r&   r9   r;   r%   rE   r   r   s    r   r'   z TraceOQLConfig.dictionary_fields   s    
 	
r    Nr(   r)   r*   r+   r,   r   r-   r   r	   r"   r'   r   r    r   r/   r/   K   sq     
c3h 
 
: 
T#tCy.%9 
 
< 
49 
 
r    r/   c                   r    e Zd ZdZedeeef   fd       Zedeeee   f   fd       Z	edee   fd       Z
y)SpanOQLConfigzOQL configuration for span filtering.

    Based on backend's SpanField enum.
    See: apps/opik-backend/src/main/java/com/comet/opik/api/filter/SpanField.java
    r   c                 z    i ddddddddddddd	d
dd
dd
dddddddddddddddddddddS )Nr1   r2   r3   r4   r5   r6   r7   r8   r9   r:   r;   r&   modelproviderr<   r=   r?   r@   rA   rB   rC   r%   rD   rF   enum)rG   rJ   typetrace_idr   r   s    r   r   zSpanOQLConfig.columns   s    
(
H
 +
 	

 X
 h
 ,
 <
 
 X
 
 #H
 F
 !(
 "8
  &x!
" 7#
$ !+ +
 	
r    c                 $   i dt         dt         dt         dt         dt         dt         dt         dd	d
gdt        dt        dt        dt        dt        dt        dt        dt        dt        t        t        t
        ddgt         dS )Nr1   r3   r7   r8   r]   r^   ra   r`   r
   r   r4   r6   r<   rA   rB   rC   rG   r9   r;   r   r   )r&   r%   r?   rJ   rP   rQ   r   s    r   r"   z!SpanOQLConfig.supported_operators   s   
"
$
 %
 &	

 %
 (
 (
 S$K
 -
 +
 #$4
 !"2
 "#3
 &'7
 (
  .!
" /#
$ -8"%~6'-
 	
r    c                 
    g dS )N)r&   r9   r;   r%   r   r   s    r   r'   zSpanOQLConfig.dictionary_fields   s    KKr    NrY   r   r    r   r[   r[      st     
c3h 
 
2 
T#tCy.%9 
 
4 L49 L Lr    r[   c                   r    e Zd ZdZedeeef   fd       Zedeeee   f   fd       Z	edee   fd       Z
y)ThreadOQLConfigzOQL configuration for thread filtering.

    Based on backend's TraceThreadField enum.
    See: apps/opik-backend/src/main/java/com/comet/opik/api/filter/TraceThreadField.java
    r   c                 "    ddddddddddddddS )Nr2   r=   r5   rD   r_   r@   )r1   first_messagelast_messagenumber_of_messagesrG   rK   rL   r4   r6   r%   statusr?   rM   r   r   s    r   r   zThreadOQLConfig.columns   s5     %$"* %*%#7$*
 	
r    c                     t         t         t         t        t        t        t        t        t        t        ddgt        t        t         dS )Nr
   r   )r1   rg   rh   ri   rG   rK   rL   r4   r6   r%   rj   r?   rM   rP   )rR   rT   rS   rV   rW   r   s    r   r"   z#ThreadOQLConfig.supported_operators   s<     #-,"2(-2-+8Dk"$2'
 	
r    c                     dgS )Nr%   r   r   s    r   r'   z!ThreadOQLConfig.dictionary_fields  s    !""r    NrY   r   r    r   re   re      sq     
c3h 
 
" 
T#tCy.%9 
 
$ #49 # #r    re   c                   r    e Zd ZdZedeeef   fd       Zedeeee   f   fd       Z	edee   fd       Z
y)DatasetItemOQLConfigzOQL configuration for dataset item filtering.

    Based on backend's DatasetItemField enum and FilterQueryBuilder.
    See: apps/opik-backend/src/main/java/com/comet/opik/api/filter/DatasetItemField.java
    r   c                     ddddddddddddS )Nr2   mapr@   r5   )r1   data	full_datasourcera   span_idr?   rK   rL   
created_bylast_updated_byr   r   s    r   r   zDatasetItemOQLConfig.columns  s/     ! %*"'
 	
r    c                 |    t         t         t         t         t         t         t         g dt        t        t        t         dS )N)r
   r   r   r   r   r   )r1   rr   rs   ra   rt   ru   rv   rq   r?   rK   rL   rP   )rR   rW   rS   r   s    r   r"   z(DatasetItemOQLConfig.supported_operators$  s2     #)&('*/W"-2'
 	
r    c                     dgS )Nrq   r   r   s    r   r'   z&DatasetItemOQLConfig.dictionary_fields5  s     xr    NrY   r   r    r   rn   rn     sq     
c3h 
 
  
T#tCy.%9 
 
  49  r    rn   c                   r    e Zd ZdZedeeef   fd       Zedeeee   f   fd       Z	edee   fd       Z
y)PromptVersionOQLConfigz/OQL configuration for prompt version filtering.r   c           
          dddddddddd	S )Nr2   r:   r@   r5   )	r1   committemplatechange_descriptionr&   r`   r?   rK   ru   r   r   s    r   r   zPromptVersionOQLConfig.columns>  s)      "*$%"

 
	
r    c                 h    t         t         t         t         t        ddgt        t        t         t         d
S )Nr
   r   )
r1   r|   r}   r~   r&   r`   r?   rK   ru   rP   )rR   rU   rW   rS   r   s    r   r"   z*PromptVersionOQLConfig.supported_operatorsL  s0     #&("2,$K"-*'
 	
r    c                     dgS )Nr&   r   r   s    r   r'   z(PromptVersionOQLConfig.dictionary_fields[  s
    |r    NrY   r   r    r   rz   rz   ;  sm    9
c3h 
 
 
T#tCy.%9 
 
 49  r    rz   r   r   c                      e Zd ZdZddee   dee   fdZedee   dd fd       Z	edee   dd fd       Z
edee   dd fd	       Zedee   dd fd
       Zedee   dd fd       Zdeeeeef         fdZdedefdZdedefdZddZdeeef   fdZdededefdZdefdZdeeef   fdZdedeeef   fdZdefdZdeeef   fdZdeeeeef         fdZy)OpikQueryLanguagea  
    This method implements a parser that can be used to convert a filter string into a list of filters that the BE expects.

    For example, this class allows you to convert the query string: `input contains "hello"` into
    `[{'field': 'input', 'operator': 'contains', 'value': 'hello'}]` as expected by the BE.

    When converting a query string into another format, a common approach is:
    1. First convert the string into a series of tokens using a tokenizer
    2. Convert the list of tokens into an Abstract Syntax Tree (AST) using a parser
    3. Traverse the AST and convert it into the desired format using a formatter

    Due to the simple nature of the queries we currently support (no support for and/or operators, etc.), we have
    combined the tokenizer and formatter steps into a single parse method.

    The parse method works by iterating over the string character by character and extracting / validating the tokens.
    Nquery_stringconfigc                     |xs d| _         |xs t        | _        d| _        | j	                         | _        d | _        | j
                  %t        j                  | j
                        | _        y y )N r   )	r   _DEFAULT_CONFIG_config_cursor_parse_expressions_filter_expressionsparsed_filtersjsondumps)r   r   r   s      r   __init__zOpikQueryLanguage.__init__w  sc    (.B0#'#:#:#< "##/"&**T-E-E"FD 0r    r   c                 $     | |t                     S )a0  
        Creates a parser for filtering traces using OQL syntax. Returns an
        OpikQueryLanguage instance preconfigured with TraceOQLConfig that validates
        trace-specific fields. Empty or None query_string yields no filters;
        malformed queries raise ValueError during parsing.
        )r/   clsr   s     r   
for_traceszOpikQueryLanguage.for_traces  s     <!122r    c                 $     | |t                     S )a-  
        Creates a parser for filtering spans using OQL syntax. Returns an
        OpikQueryLanguage instance preconfigured with SpanOQLConfig that validates
        span-specific fields. Empty or None query_string yields no filters;
        malformed queries raise ValueError during parsing.
        )r[   r   s     r   	for_spanszOpikQueryLanguage.for_spans  s     <11r    c                 $     | |t                     S )a9  
        Creates a parser for filtering trace threads using OQL syntax. Returns an
        OpikQueryLanguage instance preconfigured with ThreadOQLConfig that validates
        thread-specific fields. Empty or None query_string yields no filters;
        malformed queries raise ValueError during parsing.
        )re   r   s     r   for_threadszOpikQueryLanguage.for_threads  s     <!233r    c                 $     | |t                     S )a  
        Creates a parser for filtering dataset items using OQL syntax. Use this when working with
        dataset views or filtering items within a dataset. Returns an OpikQueryLanguage instance
        preconfigured with DatasetItemOQLConfig that validates dataset-specific fields like input,
        expected_output, and item metadata. Empty or None query_string yields no filters; malformed
        queries raise ValueError during parsing.
        )rn   r   s     r   for_dataset_itemsz#OpikQueryLanguage.for_dataset_items  s     <!5!788r    c                 $     | |t                     S )a  
        Creates a parser for filtering prompt versions using OQL syntax. Use this when searching
        or filtering prompt version history. Returns an OpikQueryLanguage instance preconfigured
        with PromptVersionOQLConfig that validates prompt version fields like tags, template,
        commit, metadata, and created_at. Empty or None query_string yields no filters; malformed
        queries raise ValueError during parsing.
        )rz   r   s     r   for_prompt_versionsz%OpikQueryLanguage.for_prompt_versions  s     <!7!9::r    c                     | j                   S N)r   r   s    r   get_filter_expressionsz(OpikQueryLanguage.get_filter_expressions  s    '''r    charc                 0    |j                         xs |dk(  S )N_)isalnumr   r   s     r   _is_valid_field_charz&OpikQueryLanguage._is_valid_field_char  s    ||~,,r    c                 "    |j                         S r   )isalphar   s     r   _is_valid_connector_charz*OpikQueryLanguage._is_valid_connector_char  s    ||~r    c                 Z   | j                   t        | j                        k  r| j                  | j                      j                         ra| xj                   dz  c_         | j                   t        | j                        k  r)| j                  | j                      j                         r_y y y y N   )r   lenr   isspacer   s    r   _skip_whitespacez"OpikQueryLanguage._skip_whitespace  s    LL3t0011!!$,,/779LLAL LL3t0011!!$,,/779 29 2r    c                     | j                   | j                     dv r4d}| j                   | j                     }| xj                  dz  c_        ||fS d}d}||fS )N)"'Tr   Fr   )r   r   )r   is_quoted_key
quote_types      r   _check_escaped_keyz$OpikQueryLanguage._check_escaped_key  sd    T\\*j8 M**4<<8JLLAL
 j(( "MJj((r    r   startc                 x   | j                   | j                     |k7  rD| j                  dz   t        | j                         k\  rt        d| j                   |dz
  d  z         y| j                  dz   t        | j                         k  r5| j                   | j                  dz      |k(  r| xj                  dz  c_        yy)N   zMissing closing quote for: r   TF)r   r   r   
ValueError)r   r   r   s      r   _is_valid_escaped_key_charz,OpikQueryLanguage._is_valid_escaped_key_char  s    T\\*j8||a3t'8'8#99 1D4E4Eeaik4RR   LL1s4#4#455!!$,,"23zA LLALr    c                    | j                   }| j                   t        | j                        k  r| j                  | j                  | j                            r`| xj                   dz  c_         | j                   t        | j                        k  r)| j                  | j                  | j                            r`| j                  || j                    }|S r   )r   r   r   r   )r   r   	connectors      r   _parse_connectorz"OpikQueryLanguage._parse_connector  s    llS!2!2338U8Udll+9
 LLAL llS!2!2338U8Udll+9
 %%edll;	r    c                 (   | j                          | j                  j                  }| j                  j                  }| j                  }| j                  t        | j                        k  r| j                  | j                  | j                           r`| xj                  dz  c_        | j                  t        | j                        k  r)| j                  | j                  | j                           r`| j                  || j                   }| j                  t        | j                        k  r| j                  | j                     dk(  r| xj                  dz  c_        | j                         \  }}| j                  }| j                  t        | j                        k  r| j                  | j                  | j                           s|r| j                  ||      ru| xj                  dz  c_        | j                  t        | j                        k  r>| j                  | j                  | j                           r`|r| j                  ||      ru| j                  || j                   }|r*|j                  |dz  |      }| xj                  dz  c_        |dk(  r!d| }||v r	|d||   dS t        d| d	      ||vr,t        d
| d| dt        |j                                d      ||v r	||||   dS ||ddS ||v r	|d||   dS |dddS )Nr   .r   r$   zusage.r   )fieldkeyr`   zWhen querying usage, zj is not supported, only usage.total_tokens, usage.prompt_tokens and usage.completion_tokens are supported.zField z# is not supported, only the fields  are supported.r2   )r   r   r   r'   r   r   r   r   r   r   replacer   r@   keys)	r   r   r'   r   r   r   r   r   composite_fields	            r   _parse_fieldzOpikQueryLanguage._parse_field  s   ,,&& LL:: llS!2!2338Q8Qdll+9
 LLAL llS!2!2338Q8Qdll+9
 !!%$,,7 LL3t0011!!$,,/36 LLAL )-(?(?(A%M:LLE,,T%6%6!77))$*;*;DLL*IJ!d&E&EjRW&X ! ,,T%6%6!77))$*;*;DLL*IJ!d&E&EjRW&X
 ##EDLL9C kkNJ ! $*3%."g-!0! ' 8  %/u  5_  ` 
 -- UG1SE)LTRYR^R^R`MaLbbqr  '!!&sGENKK "'sHEEg"2wu~FF #2x@@r    parsed_fieldc           	      `   | j                          | j                  j                  }||vrd}| j                  | j                     dk(  r:d}| xj                  dz  c_        |||   vrt        d| d| d||    d      d|iS | j                  | j                     d	v r| j                  | j                  dz      dk(  r2| j                  | j                      d}| xj                  d
z  c_        n.| j                  | j                     }| xj                  dz  c_        |||   vrt        d| d| d||    d      d|iS | j                  }| j                  t        | j                        k  r| j                  | j                     j                         s_| xj                  dz  c_        | j                  t        | j                        k  r(| j                  | j                     j                         s_| j                  || j                   }|||   vrt        d| d| d||    d      d|iS )NrP   r
   r   z	Operator z is not supported for field z, only the operators r   operator)r   r   r   )r   r   r"   r   r   r   r   r   )r   r   r"   r   r   s        r   _parse_operatorz!OpikQueryLanguage._parse_operator:  s   "ll>>22$L T\\*c1HLLAL2<@@ z)El^Shi|  ~J  jK  iL  L[  \  ))t||,
:  !12c9"//=>a@!,,T\\:!2<@@ z)El^Shi|  ~J  jK  iL  L[  \  ))LLEs4#4#455))$,,7??A! s4#4#455))$,,7??A ((>H2<@@ z)El^Shi|  ~J  jK  iL  L[  \  ))r    c                    | j                   }| j                   t        | j                        k  r| j                  | j                      j                         r_| xj                   dz  c_         | j                   t        | j                        k  r(| j                  | j                      j                         r_| j                  || j                    S r   )r   r   r   isdigit)r   r   s     r   _get_numberzOpikQueryLanguage._get_numberi  s    LL3t0011!!$,,/779LLAL LL3t0011!!$,,/779   66r    c                    | j                          | j                  }| j                  | j                     dk(  r| xj                  dz  c_        | j                  }| j                  t        | j                        k  rp| j                  | j                     dk7  rT| xj                  dz  c_        | j                  t        | j                        k  r| j                  | j                     dk7  rT| j                  || j                   }| xj                  dz  c_        d|iS | j                  | j                     j	                         s| j                  | j                     dk(  r}| j                         }| j                  t        | j                        k  rG| j                  | j                     dk(  r+| xj                  dz  c_        |d| j                         z   z  }d|iS t        d| j                  || j                    d      )Nr   r   value-r   zInvalid value z:, expected an string in double quotes("value") or a number)r   r   r   r   r   r   r   )r   r   r   s      r   _parse_valuezOpikQueryLanguage._parse_valuer  s   T\\*c1LLALLLE s4#4#455%%dll3s:! s4#4#455%%dll3s: %%edll;E LLALU##dll+335  .#5$$&Es4#4#455%%dll3s:!t//111U## !2!254<<!H I  JD  E r    c                 J   t        | j                        dk(  ry g }	 | j                         }| j                  |d         }|j	                  dd      }|t
        v rddi}n| j                         }|j                  i |||       | j                          | j                  t        | j                        k  ri| j                  }| j                         }|j                         dk(  r|j                         dk(  rt        d      t        d	| j                  |d         	 |S )
Nr   r   r   r   r   andorz4Invalid filter string, OR is not currently supportedz+Invalid filter string, trailing characters )r   r   r   r   getOPERATORS_WITHOUT_VALUESr   appendr   r   r   lowerr   )r   expressionsr   parsed_operatoroperator_nameparsed_valuepositionr   s           r   r   z$OpikQueryLanguage._parse_expressions  s4   t  !Q&,,.L #22<3HIO+//
B?M 88 '}#002R,R/R\RS!!#||c$"3"344<< 113	??$-__&$.$N  %EdFWFWX`XaFbEcd  r    r   )r   N) r(   r)   r*   r+   r   r-   r   r   classmethodr   r   r   r   r   r	   r   r   r   boolr   r   r   r   r   intr   r   r   r   r   r   r   r   r    r   r   r   e  s   "GXc] GHY<O G 3hsm 38K 3 3 2Xc] 27J 2 2 4x} 49L 4 4 9Xc] 9?R 9 9 ;x} ;AT ; ;(d38n1E(F (- - -S T 	)E$)$4 	)S   *# JAd38n JAX-*C -*DcN -*^7S 7$d38n $L)HT$sCx.-A$B )r    r   )r+   r   abcr   r   typingr   r   r   r   r	   rR   rS   rT   rV   rW   rU   r   r/   r[   re   rn   rz   r   r   r   r   r    r   <module>r      s   
  # 3 3	  8 4 	  8 8*L
Y L
^>LI >LB.#i .#b,9 ,^"Y "J '7  "\ \r    