
    4(i%                      U d dl mZ d dlZd dlZd dlZd dlZd dlZd dl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mZmZ d dlZd dlZd dlZd dlmZ e                    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!          Ze                    d"d"d#d$d$d%d&d&d&d'd"d(d'd)          Z ej        d*          Zd+Z  e! ej"        d,d-                    Z# e! ej"        d.d/                    Z$ ej"        d0d1          Z%d2Z&d3Z' e( ej"        d4d5                    Z) ej"        d6d          *                                +                                d7v Z, ej-        e.          Z/dd;Z0dd<Z1dd=Z2dd>Z3dd?Z4dd@Z5ddDZ6ddEZ7ddGZ8ddJZ9ddOZ:ddQZ;ddRZ<ddTZ=ddZZ>dd]Z?dd_Z@d dbZAddfZBddhZCeeeeef         geeDee         f         f         ZEddjZFddkZGdlddmdnddvZHddwZIddyZJddzZKdd{ZLdd|ZMdd}ZNdd~ZOeKeLeMe:eNeNeOdZPdeQd<   ddZRddZSd	dZTd
dZUddZVddZWddZXddZYddZZdddZ[dd d dddZ\dddZ]dde#e$dddZ^ G d d          Z_ G d de_          Z` G d de_          ZadabdeQd<    ejc                    ZdddZeddZfe
 G d d                      Zge
 G d d                      ZhddZii ZjdeQd<    ejc                    ZkddZl G d d          ZmddτZnddӄZoddՄZp	 ddd܄Zqdd߄ZrddddddddddddddZsdS (      )annotationsN)	dataclassfield)DictAnyListOptionalTupleIterableCallable)YOLOu   ۰0u   ۱1u   ۲2u   ۳3u   ۴4u   ۵5u   ۶6u   ۷7u   ۸8u   ۹9u   ٠u   ١u   ٢u   ٣u   ٤u   ٥u   ٦)u   ٧u   ٨u   ٩u   یu   کu   هu   وu   ا u   لا)u   يu   ىu   كu   ةu   ۀu   ؤu   إu   أu   ٱu   ءu   ئu   ﻻu   ـz/[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]u   ‌KYC_OCR_MAX_DIM2200KYC_OCR_JPEG_QUALITY98KYC_EDENAI_URLz'https://api.edenai.run/v3/universal-ai/zocr/ocr/googlezeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNTc1MDVlYTgtOGI3Yy00ZDQyLTlhYmMtOGMxYzliOGEyZDRjIiwidHlwZSI6ImFwaV90b2tlbiJ9.PLCBmy1gNDTpqfzCJwfQlaIVY4EkcE0TL7lsXiOHTMYKYC_EDENAI_TIMEOUT_SEC40!KYC_EDENAI_SHOW_ORIGINAL_RESPONSE)r   trueyesonsstrreturnc                V    t          j        dd| pd                                          S )Nz\s+ r   )resubstripr$   s    )/var/www/html/kyc_policy_engine_llm_v2.pycollapse_spacesr.   H   s$    6&#R00111    c                :    | pd                     t                    S )Nr   )	translate_FA_AR_TO_EN_DIGITSr,   s    r-   to_en_digitsr3   L   s    G2333r/   c                    | pd} t          |           } |                     t                    } |                     t          d          } t
                              d|           } t          |           S )Nr   r(   )r3   r1   _AR_FA_LETTERSreplace_ZWNJ_DIACRITICS_REr*   r.   r,   s    r-   normalize_fa_textr9   P   s]    	
bAQA	N##A			%A2q!!A1r/   c                    t          |           } t          j        dd| t          j                  } t	          |                                           S )Nz0[^\w\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\s\-]r   flags)r9   r)   r*   UNICODEr.   lowerr,   s    r-   normalize_namer?   Y   sF    !A
BBQSQ[\\\A1##%%%r/   c                L    t          |           } t          j        dd|           S )Nz[^0-9]r   )r3   r)   r*   r,   s    r-   normalize_digits_onlyrA   _   s!    QA6)R###r/   c                p    t          |           } t          j        dd|                                           S )Nz[^0-9A-Za-z]r   )r3   r)   r*   upperr,   s    r-   normalize_id_alphanumrD   d   s-    QA6/2q))//111r/   abfloatc                   | pd} |pd}| |k    rdS | r|sdS t          t          t          |          dz                       }t          | d          D ]p\  }}|g}t          |d          D ]T\  }}||k    rdnd}|                    t          ||         dz   ||dz
           dz   ||dz
           |z                        U|}q|d         }	d|	t          t          |           t          |                    z  z
  S )Nr         ?           )startr   )listrangelen	enumerateappendminmax)
rE   rF   previcacurjcbcostdists
             r-   levenshtein_ratior]   m   s'   	RA	RAAvvs A sc!ffqj!!""D1A&&&  2cq*** 	M 	MEArb11aDJJs47Q;AE
QQUd8JKKLLLL8D$SVVSVV,,,--r/   c                Z   t          |           } t          |          }| r|sdS t          |                                           }t          |                                          }t          ||z            }t          ||z            }|r||z  nd}d|z  dt	          | |          z  z   S )NrJ   333333?g?)r?   setsplitrP   r]   )rE   rF   sasbinterunionrY   s          r-   token_set_similarityrf      s    qAqA A s	QWWYYB	QWWYYBRLLERLLE'CA7S,Q22222r/   Optional[Tuple[int, int, int]]c           	        t          | pd          } |                     dd                              dd          } t          j        d|           }t	          |          dk     rdS d |dd         D             }|d	         |d
         |d         f|d	         |d         |d
         f|d
         |d	         |d         f|d
         |d         |d	         f|d         |d	         |d
         f|d         |d
         |d	         fg}|D ]=\  }}}d|cxk    rdk    r*n d
|cxk    rdk    rn &d
|cxk    rdk    r
n 6|||fc S >dS )z~
    Accepts common formats and returns (YYYY, M, D) if parseable.
    This is only for validation/matching, not for OCR.
    r   -/.z\d{1,4}   Nc                ,    g | ]}t          |          S  int.0xs     r-   
<listcomp>z$parse_date_loose.<locals>.<listcomp>   s    %%%qCFF%%%r/   r   rK        i        )r3   r6   r)   findallrP   )r$   numsvalspermsymds          r-   parse_date_looser      s   
 	QW"A			#s##C--A:j!$$D
4yy1}}t%%D!H%%%D	a$q'47#	a$q'47#	a$q'47#	a$q'47#	a$q'47#	a$q'47#E   1a1a2!q,,,,B,,,,,q!94r/   codeboolc                <   t          |           } t          |           dk    rdS t          t          |                     dk    rdS d | D             t          fdt	          d          D                       }|dz  }d         }|dk     r||k    S |d|z
  k    S )	N
   FrK   c                ,    g | ]}t          |          S rn   ro   rr   cs     r-   rt   z/iran_national_code_is_valid.<locals>.<listcomp>       ###c!ff###r/   c              3  4   K   | ]}|         d |z
  z  V  dS )r   Nrn   )rr   rV   digitss     r-   	<genexpr>z.iran_national_code_is_valid.<locals>.<genexpr>   s0      33QF1Ia 333333r/   	      ru   )rA   rP   r`   sumrO   )r   r$   rcheckr   s       @r-   iran_national_code_is_validr      s     &&D
4yyBu
3t99~~u##d###F3333%((33333A	BA1IE1uuzR!Vr/   
value_normconstraintsDict[str, Any]Tuple[bool, Optional[str]]c                >   t          |           }t          |          dk    rt          d          S |                    dd          r|dd         dk    rt          d          S |                    d	d          r|d
d         dk    rt          d          S |dd
         }	 t	          |dd                   }t	          |dd                   }t	          |dd                   }n# t
          $ r t          d          cY S w xY wd|cxk    rdk    r	n n|dz
  }n"d|cxk    rdk    rn n|}nt          d          S d|cxk    rd
k    sn t          d          S ddddddddddddd}d|cxk    r||         k    sn t          d          S t                      S )z
    Indonesia NIK (KTP/e-KTP) validation:
    - Must be 16 digits
    - DOB segment: positions 7-12 (DDMMYY) where DD+40 indicates female
    - No checksum (structural validation only)
       
nik_lengthreject_region_000000FN   000000nik_region_all_zeroreject_serial_0000rw   0000nik_serial_all_zeror   ru      nik_dob_parse)   G   (   rK   rx   nik_day_rangenik_month_range      )rK   ru   rl   r      r         r   r   r   rw   nik_day_invalid)rA   rP   _v_failgetrp   	Exception_v_ok)	r   r   vdobddmm_yydd_real
month_dayss	            r-   validate_id_nikr      s    	j))A
1vv|||$$$ -u55 .!BQB%8:K:K,---+U33 ."R%F8J8J,--- AbD'C(QqS]]QqS]]#ac(mm ( ( ('''''( 
R~~~~2~~~~~r'	
bB'''MMMMrMMMM())) B2"rbRUW]_eghhJ****JrN****()))77Ns   #AC) )DDcountryc                f    | pd                                                                 }|dk    rdS dS )zj
    Conservative OCR locale hint. Keep this deterministic; override via policy when you know better.
    r   IRfaen)r+   rC   )r   r   s     r-   default_locale_hintr      s8     
B%%''ADyyt4r/   c                 Z    t                                           } | st          d          | S )Nz"Missing hardcoded Eden AI API key.)_EDENAI_API_KEYr+   
ValueErrorkeys    r-   _get_edenai_api_keyr      s/    



!
!C ?=>>>Jr/   locale_hintc                    | pd                                                                 }|sdS |                    dd          d                             dd          d         }|sdS |S )Nr   r   ri   rK   r   _)r+   r>   ra   )r   lhbases      r-   _edenai_language_from_localer      sp    

	"	"	$	$	*	*	,	,B t88CA$$S!,,Q/D tKr/   datar   keysIterable[str]	List[Any]c                N    d |D             g dfd |            S )Nc                P    h | ]#}t          |                                          $S rn   )r%   r>   )rr   ks     r-   	<setcomp>z(_deep_values_for_keys.<locals>.<setcomp>
  s&    +++c!ffllnn+++r/   noder   r&   Nonec                >   t          | t                    r_|                                 D ]H\  }}t          |                                          v r                    |            |           Id S t          | t                    r| D ]} |           d S d S N)
isinstancedictitemsr%   r>   rR   rN   )r   r   r   rs   keysetoutwalks       r-   r   z#_deep_values_for_keys.<locals>.walk  s    dD!! 	

  1q66<<>>V++JJqMMMQ  d## 	  Q	 	 r/   )r   r   r&   r   rn   )r   r   r   r   r   s     @@@r-   _deep_values_for_keysr   	  sY    ++d+++FC        	DJJJJr/   r   Optional[str]c                V    t          | t                    rt          |           }|pd S d S r   )r   r%   r.   )r   r$   s     r-   _as_nonempty_textr     s0    !S AyD4r/   Optional[float]c                    	 t          |           }n# t          $ r Y d S w xY w|dk    r|dk    r|dz  }|dk     rd}|dk    rd}|S )NrI   g      Y@rJ   )rG   r   )r   r   s     r-   
_to_conf01r   "  sq    !HH   tt3ww1::I3ww3wwHs    
  output%Tuple[Optional[str], Optional[float]]c                B   d}d}d }t          | |          D ]7}t          |          }|r$| t          |          t          |          k    r|}8g }t          | |          D ](}t          |          }||                    |           )|rt          |          nd }||fS )N)text	full_textraw_textocr_textcontenttranscriptionvalue)
confidencescoreocr_confidenceglobal_scoreprobability)r   r   rP   r   rR   rT   )	r   	text_keys	conf_keys	best_textvvtconfsr   	best_confs	            r-   _extract_eden_text_confr   0  s    bIXI#I#FI66  b!! 	)#s1vvI'>'>IE#FI66  rNN=LLOOO#-E


Iir/   image_data_urllanguageapi_keyc                V   	 dd l }n"# t          $ r}t          d          |d }~ww xY wd| dd}t          | |dt          d}|                    t          ||t                    }	 |                                }n+# t          $ r}t          d	|j	         d
          |d }~ww xY w|
                    d          dk    rF|
                    d          p|
                    d          p	d|j	         }	t          d|	           |
                    d          }
t          |
          S )Nr   z-requests package is required for Eden AI OCR.zBearer zapplication/json)AuthorizationzContent-Type)filer   )modelinputshow_original_response)headersjsontimeoutz#Eden AI response is not JSON (http=z).statussuccesserrormessagehttp_zEden AI OCR failed: r   )requestsr   RuntimeError_EDENAI_MODEL_EDENAI_SHOW_ORIGINAL_RESPONSEpost_EDENAI_URL_EDENAI_TIMEOUT_SECr  status_coder   r   )r   r   r   r  er  payloadrespr   errr   s              r-   _call_edenai_ocr_singler  D  s   S S S SJKKQRRS -7,,* G
 " 
 
 #A G ==gGM`=aaD^yy{{ ^ ^ ^UAQUUUVV\]]^ xxY&&hhwT488I#6#6T:T$BR:T:T7#77888XXhF"6***s(    
&!&(A= =
B%B  B%request_bundlec           	        t                      }t          |                     d          pd          }t          |          }i }|                     d          pg }|D ]|}t	          |t
                    st           |j        d          pd                                          }|sM |j        d          pg }d}	d}
d}d	}t          |          D ]\  }}t	          |t
                    s|                    d
          }t	          |t                    r|                                sZ	 t          |||          \  }}n4# t          $ r'}t                              d|||           Y d}~d}~ww xY wd}|r|dz  }|||z  }||k    r<|}	 t          |                    d                    }	n# t          $ r |}	Y nw xY w|}
|}|	|
||nddd||<   ~d|iS )zw
    Adapter for KYC bundle -> Eden AI OCR.
    Returns response in the same shape expected by extract_with_llm().
    r   r   fieldsr   r   
candidatesr   Ng      r   )r   r   r   z+Eden OCR failed for key=%s candidate=%s: %srJ         ?idxcandidate_idxr   r   
normalized)r   r%   r   r   r   r   r+   rQ   r  r   _LOGwarningrp   )r  r   r   r   
out_fieldsr  r   r   r  best_idxr   r   
best_scorerV   candr   r   confr  r   s                       r-   eden_llm_ocrr)  e  so   
 "##Gn((77?4@@K+K88H!#J))/RF 3
 3
%&& 	)%)E""(b))//11 	UY|,,2
#'	%)	
 ,, 	! 	!GAtdD)) !XX&677Nnc22 .:N:N:P:P 4#1%#  
dd
    JCQRTUVVV E z!!"
!"488E??33HH  ! ! ! HHH! 	 	 &(1(=993	
 

3 j!!s*   .E
E5E00E5"F44GGOptional[List[int]]c                   | d S t          | t                    r>g }| D ]7}	 |                    t          |                     &# t          $ r Y  d S w xY w|S t          | t
                    r|                                 }|sd S |                    d          r_|                    d          rJ	 t          j
        |          }t          |t                    rt          |          S n# t          $ r Y nw xY wt          j        d|          }g }|D ]:}|s	 |                    t          |                     )# t          $ r Y  d S w xY w|S d S )N[]z[\s,;]+)r   rN   rR   rp   r   r%   r+   
startswithendswithr  loads_parse_int_listr)   ra   )r   r   rs   r$   arrpartsps          r-   r1  r1    s   yt!T  	 	A

3q66""""   ttt
!S GGII 	4<< 	C 	jmmc4(( 0*3///0   Q'' 	 	A 

3q66""""   ttt
4s5   "A
AA07C) )
C65C6"D;;
E
	E
c                :   t          |           } t          |           dk     rdS d | D             }d}t          |          dz
  dz  }t          |dd                   D ])\  }}|dz  |k    r|dz  }|dk    r|dz  }||z  }$||z  }*|d         }||z   dz  dk    S )	zStandard Luhn mod-10 checksum.ru   Fc                ,    g | ]}t          |          S rn   ro   r   s     r-   rt   z!luhn_is_valid.<locals>.<listcomp>  r   r/   r   NrM   r   r   )rA   rP   rQ   )r   r   checksumparityrV   r   r   r   s           r-   luhn_is_validr9    s     &&D
4yy1}}u##d###FH&kkAo"F&"+&&  1q5F??QBAvvaNHHMHH2JE#))r/   r   mod_minus_r)modcheck_index
check_ruleweights	List[int]r;  rp   r<  Optional[int]r=  c                  t          |           } | sdS d | D             t                    }|dk     rdS ||dz
  nt          |          dk     r|z   dk     s|k    rdS          }fdt          |          D             }t          |          t          |          k    rdS t	          d t          ||          D                       }|t          |          z  }	|pd	                                                                }
|
d
k    r|	dk     r|	nt          |          |	z
  }n2|
dv r#t          |          |	z
  t          |          z  }n|
dk    r|	}ndS t          |          t          |          k    S )a  
    Generic weighted mod-11 validator.

    Parameters are taken from constraints:
      - weights: list[int] for all digits EXCEPT the check digit.
      - mod: default 11
      - check_index: index of check digit (default last)
      - check_rule:
          * "iran"        : expected = r if r < 2 else (11 - r)
          * "mod_minus_r" : expected = (mod - r) % mod
          * "r"           : expected = r
    Fc                ,    g | ]}t          |          S rn   ro   r   s     r-   rt   z+mod11_weighted_is_valid.<locals>.<listcomp>  r   r/   ru   NrK   r   c                ,    g | ]}|k    |         S rn   rn   )rr   rV   cir   s     r-   rt   z+mod11_weighted_is_valid.<locals>.<listcomp>  s"    :::!r''6!9'''r/   c              3  &   K   | ]\  }}||z  V  d S r   rn   )rr   r   ws      r-   r   z*mod11_weighted_is_valid.<locals>.<genexpr>  s*      88daAE888888r/   r   iran>   mod-11r:  r   )rA   rP   rp   rO   r   zipr+   r>   )r   r>  r;  r<  r=  ncheck_digitdata_digitsr$   r   ruleexpectedrD  r   s               @@r-   mod11_weighted_is_validrO    s   ( !&&D u##d###FFA1uuu'!a%%S-=-=B	AvvV	Avvqu*K:::::eAhh:::K
7||s;''''u88c+w7788888A	CHHA"##%%++--Dv~~A11CHHqL	*	*	*HHqLCHH,	u{s8}},,r/   c                     dS )NTNrn   rn   r/   r-   r   r      s    :r/   reasonc                
    d| fS )NFrn   rR  s    r-   r   r   $  s    &=r/   c                Z    t          |           rt                      S t          d          S )Niran_national_code_invalid)r   r   r   r   r   s     r-   validate_iran_national_coderX  (  s*    ":.. ww/000r/   c                Z    t          |           rt                      S t          d          S )Nluhn_invalid)r9  r   r   rW  s     r-   validate_luhnr[  .  s)    Z   ww>"""r/   c           	        t          |                    d          p|                    d                    }|st          d          S |                    dd          }|                    d          }|                    dd          }	 t          | |t	          |          |t	          |          nd t          |          	          }n# t          $ r d
}Y nw xY w|rt                      S t          d          S )Nr>  weight_listmod11_missing_weightsr;  r   r<  r=  r:  )r>  r;  r<  r=  Fmod11_invalid)r1  r   r   rO  rp   r%   r   r   )r   r   r>  r;  r<  r=  oks          r-   validate_mod11_weightedra  4  s	   kooi88ZKOOM<Z<Z[[G 0.///
//%
$
$C//-00K}==J	$C-8-D[)))$:
 
 
     
 ww?###s   ?C CCc                    t                      S r   )r   rW  s     r-   validate_nonerc  M  s    77Nr/   c                     t          d          S )Nidentity_rule_missing)r   rW  s     r-   validate_missing_identity_rulerf  Q  s    *+++r/   )iran_national_codeluhnmod11_weightedid_niknoneskipmissing_identity_rulezDict[str, ValidatorFn]_VALIDATOR_REGISTRYvalidator_namec                    | pd                                                                 }|st                      S t                              |          }|t          d|           S  ||pd|pi           S )z
    Dispatches to a deterministic validator.
    Unknown validator names are treated as invalid (safer than silently skipping).
    r   Nzunknown_validator:)r+   r>   r   rn  r   r   )ro  r   r   namefns        r-   run_validatorrs  `  s    
  b''))//11D ww		 	 	&	&B	z2D223332jB 1r222r/   quad
np.ndarrayscalec                    |                      t          j                  } |                     dd          }|| |z
  t	          |          z  z   S )Nr   T)axiskeepdims)astypenpfloat32meanrG   )rt  rv  r   s      r-   expand_quadr~  s  sD    ;;rz""D		q4	((AqE%LL(((r/   ptsc                   |                      t          j                  } |                     d          }| t          j        |                   }| t          j        |                   }t          j        | d                              d          }| t          j        |                   }| t          j        |                   }t          j        ||||gt          j                  S )NrK   )rx  rM   dtype)	rz  r{  r|  r   argminargmaxdiffreshapearray)r  r$   tlbrr   trbls          r-   order_points_clockwiser  x  s    
**RZ
 
 CQA	RYq\\	B	RYq\\	B
!$$R((A	RYq\\	B	RYq\\	B8RR$BJ7777r/   rF  hc                    t          j        | d d df         d|dz
            | d d df<   t          j        | d d df         d|dz
            | d d df<   | S )Nr   rK   )r{  clip)r  rF  r  s      r-   clip_pointsr    se    AAAqD	1a!e,,C1IAAAqD	1a!e,,C1IJr/   img_bgrc                X   | j         d d         \  }}|                    t          j                  }t	          |||          }t          |          }|\  }}}}t          j                            ||z
            }t          j                            ||z
            }	t          t          ||	                    }
t          j                            ||z
            }t          j                            ||z
            }t          t          ||                    }t          |
d          }
t          |d          }t          j
        ddg|
dz
  dg|
dz
  |dz
  gd|dz
  ggt          j                  }t          j        ||          }t          j        | ||
|ft          j                  S )Nru   r   rK   r  r;   )shaperz  r{  r|  r  r  linalgnormrp   rT   r  cv2getPerspectiveTransformwarpPerspectiveINTER_CUBIC)r  rt  HWr  r  r  r  widthAwidthBmaxWheightAheightBmaxHdstMs                   r-   warp_quad_to_rectr    sl   =!DAq;;rz""DtQ""D!$''DNBBY^^BG$$FY^^BG$$Fs66""##DinnR"W%%GinnR"W%%Gs7G$$%%DtQ<<DtQ<<D
(QFTAXqMD1HdQh+?!TAXOWYWa
b
b
bC#D#..AwD$<sOOOOr/   c                   t          j        | t           j                  }t          j        |ddt           j                  \  }}|j        dd         \  }}t          dt          t          dt          ||          z                                }t          j	        |||||t           j
        d          }t          j        |t           j                  S )zj
    Classic threshold preprocessing (requested):
    - Convert to gray
    - BINARY threshold at 120
    x      Nru   rw   Q?r   r  cvtColorCOLOR_BGR2GRAY	thresholdTHRESH_BINARYr  rT   rp   roundcopyMakeBorderBORDER_CONSTANTCOLOR_GRAY2BGRr  grayr   threshr  rF  pads          r-   apply_numeric_ocr_filter_cv2r    s     <!344DdCc.?@@IAv<DAq
b#eD3q!99,--..
/
/CS#sC<OWZ[[[F< 2333r/   c           	     |   t          j        | t           j                  }d}d}t          j        |||          }t	          j        g dg dg dg          }t          j        |d|          }|j        \  }}d}	t          j        ||	|	|	|	t           j	        d          }
t          j        |
t           j
                  S )	zE
    Apply Grayscale + Contrast + Sharpening for better LLM OCR.
    gffffff?r   )alphabetar   rM   r   rM   r   rM   rM   r  r  )r  r  r  convertScaleAbsr{  r  filter2Dr  r  r  r  )r  r  r  r  contrastkernelsharpr  rF  r  paddeds              r-   apply_smart_ocr_filter_cv2r    s    
 <!344D ED"4u4@@@H Xzzz"{{!zz# $ $F L2v..E ;DAq
CsCc3;NVYZZZF < 2333r/   c                   t          j        | t           j                  }t          j        |ddt           j                  \  }}|j        dd         \  }}t          dt          t          dt          ||          z                                }t          j	        |||||t           j
        d          }t          j        |t           j                  S )z:Simple binary threshold for card OCR (requested approach).r  r  Nru   rw   r  r  r  r  s          r-   apply_adaptive_ocr_thresholdr    s    <!344DdCc.?@@IAv<DAq
b#eD3q!99,--..
/
/CS#sC<OWZ[[[F< 2333r/   imgc                l    t          j        g dg dg dg          }t          j        | d|          }|S )z(Sharpen OCR input with requested kernel.r  r  rM   )r{  r  r  r  )r  r  	sharpeneds      r-   apply_sharp_ocr_filter_cv2r    sG    Xzzz!kk jj" # #F S"f--Ir/   rv   max_dimc           	     t   | j         d d         \  }}t          ||          }||k    r| S |t          |          z  }t          dt          t	          ||z                                }t          dt          t	          ||z                                }t          j        | ||ft
          j                  S )Nru   interpolation)r  rT   rG   rp   r  r  resize
INTER_AREA)r  r  r  rF  r~   rv  nwnhs           r-   _resize_max_dimr    s    9RaR=DAqAq		AG||
eAhhE	QE!e)$$%%	&	&B	QE!e)$$%%	&	&B:cB83>BBBBr/   ix  r  min_hmin_wr  r  c                  | j         d d         \  }}t          ||          }||k    r|t          |          z  }t          j        | t          dt          t          ||z                                t          dt          t          ||z                                ft          j                  } | j         d d         \  }}d}|r&||k     r t          ||t          |          z            }|r&||k     r t          ||t          |          z            }|dk    r]t          j        | t          t          ||z                      t          t          ||z                      ft          j                  } | S )Nru   r  rI   g)\(?)	r  rT   rG   r  r  rp   r  r  r  )	r  r  r  r  r  rF  r~   r$   s_ups	            r-   _resize_to_ranger    sO   9RaR=DAq 	Aq		A7{{eAhhjs1c%!**oo66As51::8O8OP`c`noooy!}1 D +U4q)** +U4q)**d{{js54==113uQtV}}3E3EFVYVefffJr/   b   r   defaultc                    	 t          |           }n# t          $ r |cY S w xY wt          dt          d|                    S )Nr   d   )rp   r   rT   rS   )r   r  qs      r-   _normalize_jpeg_qualityr    sP    JJ   r3sA;;s    !!r   smartkindvariantr  jpeg_qualityr  r  r  c                  |pd                                 }|pd                                 }|dv rt          | |dd          } nt          | |dd          } |d	k    rt          |           }nO|d
k    rt          |           }n9|dk    r$|dv rt          |           }nt	          |           }nt          |           }t          |t                    }t          j	        d|t          j
        |g          \  }}|st          d          t          j        |                                                              d          }	d|	 S )Nr   r  numericdate   iX  r  r  r   rawsharpenr  .jpgzcv2.imencode(.jpg) failedasciizdata:image/jpeg;base64,)r>   r  r  r  r  r  r  _OCR_JPEG_QUALITYr  imencodeIMWRITE_JPEG_QUALITYr  base64	b64encodetobytesdecode)
r  r  r  r  r  	processedr  r`  bufb64s
             r-   _encode_image_for_llmr    sY    NF!!##D!'((**G""""7G3cRRR"7G3aPPP%.w77			I		.w77			H		&&&4W==II4W==II.w77	.?@@Al69s/G.KLLGB 86777

3;;==
)
)
0
0
9
9C*S***r/   c                      e Zd ZddZdS )	FaceMatcherBasedoc_photo_bgrru  
selfie_bgrr&   r   c                    t                      r   )NotImplementedErrorselfr  r  s      r-   matchzFaceMatcherBase.match*  s    !###r/   Nr  ru  r  ru  r&   r   __name__
__module____qualname__r   rn   r/   r-   r  r  )  s(        $ $ $ $ $ $r/   r  c                      e Zd ZddZdS )	FaceMatcherUnavailabler  ru  r  r&   r   c                    d d dd dS )Nface_module_unavailablescore01cosinerR  detailsrn   r  s      r-   r   zFaceMatcherUnavailable.match/  s    4;Taefffr/   Nr  r  rn   r/   r-   r  r  .  s.        g g g g g gr/   r  c                  N    e Zd ZdddZed             Zedd            ZddZdS )InsightFaceMatcher  r  Fdet_sizeTuple[int, int]
prefer_gpur   c                    ddl m} |rddgndg} |dd|          | _        | j                            d|	           d S )
Nr   )FaceAnalysisCUDAExecutionProviderCPUExecutionProvider	buffalo_lz/var/www/html/runtime)rq  root	providersrM   )ctx_idr  )insightface.appr  appprepare)r  r  r  r  r  s        r-   __init__zInsightFaceMatcher.__init__4  si    000000ISq,.DEEZpYq	<[6MYbcccX66666r/   c                2    | sd S d }t          | |          S )Nc                J    | j         \  }}}}t          ||z
  ||z
  z            S r   )bboxrG   )fx1y1x2y2s        r-   areaz.InsightFaceMatcher._largest_face.<locals>.area?  s,    VNBB"r'b2g.///r/   r   )rT   )facesr)  s     r-   _largest_facez InsightFaceMatcher._largest_face:  s3     	4	0 	0 	0 5d####r/   rE   ru  rF   r&   rG   c                V   |                      t          j                  } |                     t          j                  }t          j                            |           dz   }t          j                            |          dz   }t          t          j        | |          ||z  z            S )Ng&.>)rz  r{  r|  r  r  rG   dot)rE   rF   nanbs       r-   _cosinezInsightFaceMatcher._cosineE  sy    HHRZ  HHRZ  Y^^A%Y^^A%RVAq\\R"W-...r/   r  r  r   c                   | j                             |          }| j                             |          }|                     |          }|                     |          }|d d ddt          |          idS |d d ddt          |          idS |                     |j        |j                  }|dz   dz  }t          |          t          |          dd dS )	Nno_face_in_document_photo	doc_facesr
  no_face_in_selfieselfie_facesrI          @r`  )r  r   r+  rP   r0  normed_embeddingrG   )	r  r  r  r3  	sel_facesfdfscosr  s	            r-   r   zInsightFaceMatcher.matchM  s   HLL//	HLL,,		**	**:#t?Zhsux  zC  vD  vD  hE  F  F  F:#t?R`npst}p~p~_  A  A  All2.0CDD9# >>U3ZZ4\`aaar/   N)r  F)r  r  r  r   )rE   ru  rF   ru  r&   rG   r  )r  r  r  r   staticmethodr+  r0  r   rn   r/   r-   r  r  3  s        7 7 7 7 7 $ $ \$ / / / \/b b b b b br/   r  zOptional[FaceMatcherBase]_FACE_MATCHERc                     t           5  t          t          cd d d            S 	 t          d          an# t          $ r t	                      aY nw xY wt          cd d d            S # 1 swxY w Y   d S )NF)r  )
_FACE_LOCKr=  r  r   r  rn   r/   r-   get_face_matcherr@  b  s    	  $        	5.%@@@MM 	5 	5 	5244MMM	5                 s1   A&5A&AA&A	A&&A*-A*doc_image_pathselfie_image_pathdoc_photo_quadOptional[List[List[float]]]c                >   |sdddddS |sdddddS t          j        |           }t          j        |          }|dddddS |dddddS t          |t          j        |t          j                            }t                      }|                    ||          S )z^
    Computes face match pack once. Caller decides whether to use it depending on policy.
    Nselfie_missingr
  doc_photo_not_detectedcannot_read_doc_imagecannot_read_selfie_imager  )r  imreadr  r{  r  r|  r@  r   )rA  rB  rC  doc_imgsel_imgcropmatchers          r-   compute_face_packrO  n  s      ^4;KX\]]] f4;S`deeej((Gj*++G4;R_cddd4;UbfgggWbh~RZ&P&P&PQQD  G==w'''r/   c                      e Zd ZU ded<   ded<   ded<   dZded	<   d
Zded<   dZded<   dZded<   dZded<   dZ	ded<   dZ
ded<    ee          Zded<   dZded<   dZded<   dZded<   d Zded!<   dZd"ed#<   dS )$	FieldRule	List[str]class_namesr%   r   r   required      ?rG   min_det_confg?min_ocr_confr   ocr_kindoptional
match_type333333?match_thresholdr   rp   expected_lenNr   	validatordefault_factoryr   r   rI   weightF
must_match
match_gaterK   max_candidateszOptional[List[str]]input_aliases)r  r  r  __annotations__rV  rW  rX  rZ  r\  r]  r^  r   r   r   ra  rb  rc  rd  re  rn   r/   r-   rQ  rQ    s        HHHNNNL LH J    !O!!!!L#I####"'%"="="=K====FJJN)-M------r/   rQ  c                  &   e Zd ZU ded<   ded<   dZded<   dZded<   dZded	<   d
Zded<   dZded<   dZ	ded<   dZ
ded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded <   dZded!<    ee"          Zd#ed$<   d%Zded&<   d'Zded(<   d
S ))	DocConfigr%   doc_idzList[FieldRule]rulesr   r   doc_typer   ocr_locale_hintNr@  min_detected_fields_countrI   rG   approve_min_coverage(\?approve_min_extractionq=
ףp?approve_min_match_corerJ   approve_min_match_all      ?review_min_coverager  reject_below_coverage皙?approve_no_input_extra_bufferFr   require_face_matchr  face_metricface_match_thresholdr_  zList[Tuple[str, str]]
swap_pairsTenable_name_swapr  name_swap_margin)r  r  r  rf  r   rk  rl  rm  rn  rp  rr  rs  ru  rv  rx  ry  rz  r{  r   rN   r|  r}  r~  rn   r/   r-   rh  rh    sg        KKK GHO/33333"%%%%%$((((($(((((#&&&&&!%%%%%#'''''+/!////  %$$$$ K    "&&&&& ).d(C(C(CJCCCC!!!!!"""""""r/   rh  r  c                   g }|                      dg           D ]}t          |          }|                     d          p|                     d          }t          |t                    r|g|d<   nt	          |pg           |d<   |                    dd            |                     d          }|i |d<   n]t          |t                    r.	 t          j        |          |d<   n/# t          $ r i |d<   Y nw xY wt          |t                    si |d<   |	                    t          d)i |           |                      d          pg }d |D             }t          d)i dt          |                      d          p|                      d          pd	          d|d
t          |                      d
          pd                                          dt          |                      d          pd          dt          |                      d          pZ|                      d          pE|                      d          p0t          t          |                      d
          pd                              d|                      dd           dt          |                      dd                    dt          |                      dd                    dt          |                      d|                      dd                              dt          |                      dd                    dt          |                      dd                    dt          |                      dd                    dt          |                      dd                    d t          |                      d d!                    d"t          |                      d"d#                    d$t          |                      d$d                    d|d%t          |                      d%d&                    d't          |                      d'd(                    S )*Nrj  rS  
class_namer   r|  c                    g | ]@}t          |t          t          f          t          |          d k    1t          |          AS )ru   )r   rN   tuplerP   rq   s     r-   rt   z+doc_config_from_payload.<locals>.<listcomp>  sF    ___q*Qu2N2N_SVWXSYSY]^S^S^%((S^S^S^r/   ri  iddocr   r   rk  rl  r1   r   rm  rn  rI   rp  ro  rr  approve_min_matchrq  rs  rJ   ru  rt  rv  r  rx  rw  ry  Frz  r  r{  r}  Tr~  r  rn   )r   r   r   r%   rN   popr  r0  r   rR   rQ  rh  rC   r   rG   r   )r  rj  r   rrcncstr|  s          r-   doc_config_from_payloadr    s   E[["%% & &!WWVVM"":bff\&:&:b# 	/!#B} $RX2B}
|T"""ff]##; "B}S!! 	#'$(JsOO=!! ' ' '$&=!!!'C&& 	# "B}Y____%%%%\**0bJ__J___J   7;;x((FGKK,=,=FGGGe GKK	**0b1177999	
 W[[,,2333 KK$$ F{{,--F{{=))F #3w{{9'='='C#D#DEE	
 
 
 #*++.I4"P"P"P #7;;/Es#K#KLLL   %W[[1I4%P%PQQQ!"  %W[[1I7;;WjlpKqKq%r%rsss#$ $GKK0G$M$MNNN%( "'++.CT"J"JKKK)* $GKK0G$N$NOOO+, ',GKK8WY],^,^&_&_&_-0  ,@% H HIII12 M9==>>>34 #7;;/Et#L#LMMM58 :9: gkk*<dCCDDD;< w{{+=tDDEEE= s   C&&C87C8zDict[str, 'KYCEngine']_ENGINE_CACHE
model_path'KYCEngine'c                    t          |           }t          5  |t          vrt          |           t          |<   t          |         cd d d            S # 1 swxY w Y   d S r   )r%   _ENGINE_LOCKr  	KYCEngine)r  r   s     r-   
get_enginer    s    
j//C	 " "m##!*:!6!6M#S!" " " " " " " " " " " " " " " " " "s   -AAAc                      e Zd Zd2dZed3d            Zd4dZd5dZeddd6d            Zed7d            Z	ed7d            Z
ed7d            Zd d d!eefd8d-Zd9d1Zd S ):r  yolo_model_pathr%   c                $   t          |          | _        | j        j        }t          |t                    r$d |                                D             | _        nd t          |          D             | _        t          j	                    | _
        d S )Nc                N    i | ]"\  }}t          |          t          |          #S rn   )rp   r%   )rr   r   r   s      r-   
<dictcomp>z&KYCEngine.__init__.<locals>.<dictcomp>  s*    III41aAAIIIr/   c                4    i | ]\  }}|t          |          S rn   )r%   )rr   rV   rJ  s      r-   r  z&KYCEngine.__init__.<locals>.<dictcomp>!  s$    GGGda3q66GGGr/   )r   r  namesr   r   r   rS  rQ   	threadingLock_infer_lock)r  r  r  s      r-   r   zKYCEngine.__init__  s    /**

 eT"" 	HII5;;==IIIDGGi6F6FGGGD$>++r/   rs   rG   r&   c           	     X    t          dt          dt          |                               S )NrJ   rI   )rT   rS   rG   rs   s    r-   _clamp01zKYCEngine._clamp01%  s"    3Cq**+++r/   rM  rQ  
user_inputDict[str, str]r   c                    |                     |j                  }|r|S |j        r%|j        D ]}|                     |          }|r|c S d S r   )r   r   re  )r  rM  r  r   akr   s         r-   _get_expectedzKYCEngine._get_expected)  sk    NN48$$ 	H 	(  ^^B'' IIItr/   
image_pathr(  ioudevicemax_detrp   List[Dict[str, Any]]c           
     :   | j         5  | j                            |||||d          }d d d            n# 1 swxY w Y   |sg S |d         }g }t          |dd           |j        |j        j        }	|j        j        }
|j        j        }t          |	t          j
                  r8|	                                                                                                }	t          |
t          j
                  r8|
                                                                                                }
t          |t          j
                  r8|                                                                                                }t          t          |	                    D ]}t!          |
|                   }|                    || j                            |t)          |                    t+          ||                   |	|                             t*                                                    d           |S t          |dd           |j        |j        j        }|j        j        }
|j        j        }t          |t          j
                  r8|                                                                                                }t          |
t          j
                  r8|
                                                                                                }
t          |t          j
                  r8|                                                                                                }t          t          |                    D ]}d ||         D             \  }}}}t!          |
|                   }||g||g||g||gg}|                    || j                            |t)          |                    t+          ||                   |d           |S )NF)sourcer(  r  r  r  verboser   obb)class_idr  r(  rt  boxesc                ,    g | ]}t          |          S rn   )rG   rq   s     r-   rt   z$KYCEngine.detect.<locals>.<listcomp>e  s    !<!<!<q%((!<!<!<r/   )r  r  predictgetattrr  xyxyxyxyclsr(  r   torchTensordetachcpunumpyrO   rP   rp   rR   rS  r   r%   rG   rz  tolistr  xyxy)r  r  r(  r  r  r  predsr   detsquadsr  cfrV   cidr  r%  r&  r'  r(  rt  s                       r-   detectzKYCEngine.detect4  s    	 	J&&!#fg_d '  E	 	 	 	 	 	 	 	 	 	 	 	 	 	 	
  	I!H%' 1eT"".153DENE%)CB%.. 5**,,2244#u|,, 1jjll&&((..00"el++ /YY[[__&&,,..3u::&&  #a&kk #"&"2"6"6sCHH"E"E!"Q%LL!!HOOE2299;;	      K 1gt$$0QW5H7<D'+CB$-- 3{{}}((**0022#u|,, 1jjll&&((..00"el++ /YY[[__&&,,..3t99%% 	 	!<!<DG!<!<!<BB#a&kkR2r(RHr2h? #"&"2"6"6sCHH"E"E!"Q%LL 	      s   !599r   )r   fields_planr   r   c                *    dd|| ddgddddd	d
d}|S )a  
        Returns a single, self-contained bundle to be sent to your LLM layer.
        You can convert it to OpenAI Responses API (or any other provider) on your server.

        fields_plan: list of:
          {
            "key": str,
            "kind": "text|numeric|date|mrz",
            "candidates": [{"idx": int, "det_conf": float, "image_data_url": str}, ...]
          }

        Expected LLM response:
          {
            "fields": {
              "<key>": {
                 "candidate_idx": 0,
                 "text": "...",          # what you read
                 "confidence": 0.0-1.0,  # optional but recommended
                 "normalized": "..."     # optional; server can re-normalize anyway
              }, ...
            }
          }
        rK   kyc_field_ocrr  r  zint (required)zstring|null (required)zfloat 0..1 (optional)zstring|null (optional)r  )typetop_level_keysfield_object)schema_versiontaskr   r  output_contractrn   )r  r   schemas      r-   build_llm_ocr_prompt_bundlez%KYCEngine.build_llm_ocr_prompt_bundles  sK    <  #&!#+*%54"9":	! !	  	 
 
  r/   bundlec           	     $   |                      d          pg }d |D             }dd                    |           }d|dg}|D ]}|                     d          }|                     d          }|                     d	          pg }d
| d| d}	|dk    r|	dz  }	|                    d|	d           |D ]A}
d|
v r;|
d                             dd          \  }}|                    ddd|dd           B|                    ddd           |S )Nr  c                8    g | ]}|                     d           S r   )r   rr   r$  s     r-   rt   zAKYCEngine.anthropic_content_items_from_bundle.<locals>.<listcomp>  s"    222aQUU5\\222r/   uv  You are a specialized OCR engine for Iranian ID cards (National Card/Shenasnameh).
CRITICAL INSTRUCTION FOR DIGITS:
The images contain Persian/Arabic numerals (۰, ۱, ۲, ۳, ۴, ۵, ۶, ۷, ۸, ۹).
You MUST transcribe them directly as standard English digits (0-9).
 - Visible: '۳۴۸' -> Output: '348'
 - Visible: '۱۴۰۲' -> Output: '1402'

OUTPUT RULES:
1. Return ONLY valid JSON. No markdown.
2. If a field is blurry or unreadable, set 'text': null.
3. Do not include any explanations.

JSON Response Structure:
{
  "fields": {
    "KEY": {"text": "value", "confidence": 0.99, "candidate_idx": 0}
  }
}
REQUIRED FIELDS: , r   r  r   r   r  r  z
Target Field: 'z	' (Type: )r  z -> EXTRACT ONLY DIGITS (0-9)r   ,rK   imager  	image/png)r  
media_typer   )r  r  z
JSON Output:)r   joinrR   ra   )r  r  	keys_listintror   r$  r   r  cands
field_hintr   r   b64_datas                r-   #anthropic_content_items_from_bundlez-KYCEngine.anthropic_content_items_from_bundle  s   H%%+226222	
7$ !%		) 4 4%7 7 	, !%001 	 	A%%,,C55==DEE,''-2E CSBB4BBBJy  ==
LL&*==>>>  
 
#q(("#$4"5";";C"C"CKAxLL '$,*5$,# #" "   
 	f.>??@@@r/   c                   |                      d          pg }d}d|dg}|D ]=}|                     d          }|                     d          }|                     d          pg }t          |                     d          pd	          }|                     d
          pd                                }	|                    dd| d| d| d|	 dt	          |           
d           |D ]u}
|
                     d          }|
                     d          }|                    dd| d| d           |                    d|
                     d          d           v?|                    ddd           |S )aL  
        Helper: convert bundle into OpenAI Responses API `input` content items in ONE request.
        (You can do the same on your server; provided for clarity.)

        Output is a list of items like:
          {"type":"input_text","text":"..."}
          {"type":"input_image","image_url":"data:image/jpeg;base64,..."}
        r  a  You are a deterministic OCR engine for KYC field crops.
Return ONLY valid JSON. No markdown.

Global rules:
- Do NOT guess. If unreadable or ambiguous: text=null and confidence<=0.3.
- Never invent missing characters.
- Preserve native letters for names (do not transliterate).

Numeric/date rules:
- For kind=numeric: output ONLY ASCII digits 0-9 (no spaces, no separators).
- For kind=date: output as seen; if you can normalize to YYYY-MM-DD, also set normalized.
- NEVER drop leading zeros.

Constraint handling (IMPORTANT):
- Each FIELD may provide expected_len and/or regex.
- If kind=numeric and expected_len>0: you MUST return exactly that many digits.
- If regex is provided: your text MUST match it exactly.
- If you cannot satisfy constraints with high confidence: text=null.

Output JSON format:
{ "fields": { "<key>": {"candidate_idx":0, "text":null, "confidence":0.0, "normalized":null} } }

input_textr  r   r  r  r]  r   regexr   
FIELD key= kind= expected_len= regex= candidates=r  det_confCandidate idx=
 det_conf=input_imager   )r  	image_urlz
Return ONLY JSON. No markdown.)r   rp   r+   rR   rP   )r  r  r  r   r$  r   r  r  r]  r  r   r  r  s                r-    openai_content_items_from_bundlez*KYCEngine.openai_content_items_from_bundle  s    H%%+} 	4 1=e'L'L&M 	\ 	\A%%,,C55==DEE,''-2Equu^449::LUU7^^)r0022ELL$zszz$zzlzz[`zznqrwnxnxzz    
  \ \eeEll55,,l<fS<f<f\d<f<fgghhhm!%%HXBYBYZZ[[[[\ 	l4VWWXXXr/   c                R   |                      d          pg }d |D             }dd                    |           }d|ig}t          j        d          }|D ]7}t	          |t
                    s|                     d          }t	          |t                    r|                                sX|                     d          pd                                }|                     d	          pg }	|                     d
          }
|                     d          pd                                }	 t          |
pd          }n# t          $ r d}Y nw xY w|                    dd| d| d| d| dt          |	           
i           |	D ]}t	          |t
                    s|                     d          }|                     d          }|                     dd          }t	          |t                    rd|vrs|                    dd| d| i           |                    |                                          }d}|r|                    d          p|}|                    dd          \  }}|                    d||di           9|                    ddi           |S )Nr  c                    g | ]a}t          |                    d           t                    *|                    d           ?t          |                    d                     bS r   )r   r   r%   r  s     r-   rt   z6KYCEngine.gemini_parts_from_bundle.<locals>.<listcomp>  sY    iii1*QUU5\\SV:W:Wi\]\a\abg\h\hiSu&&iiir/   a  You are a deterministic OCR engine for KYC field crops.
Return ONLY valid JSON. No markdown.

Global rules:
- Do NOT guess. If unreadable or ambiguous: text=null and confidence<=0.3.
- Never invent missing characters.
- Preserve native letters for names (do not transliterate).

Numeric/date rules:
- For kind=numeric: output ONLY ASCII digits 0-9 (no spaces, no separators).
- For kind=date: output as seen; if you can normalize to YYYY-MM-DD, also set normalized.
- NEVER drop leading zeros.

Constraint handling (IMPORTANT):
- Each field may provide expected_len and/or regex.
- If kind=numeric and expected_len>0: you MUST return exactly that many digits.
- If regex is provided: your text MUST match it exactly.
- If you cannot satisfy constraints with high confidence: text=null.

Output JSON format:
{"fields": {"<key>": {"candidate_idx":0, "text": null, "confidence":0.0, "normalized": null}}}
REQUIRED FIELDS: r  r   z^data:([^;]+);base64,r   r  r  r]  r  r   r   r  r  r  r  r  r  r  r   zbase64,r  r  r  rK   inline_data)	mime_typer   zReturn ONLY JSON. No markdown.)r   r  r)   compiler   r   r%   r+   r>   rp   r   rR   rP   r   groupra   )r  r  r  r  r3  data_url_rer$  r   r  r  r]  r  expected_len_ir   r  r  r   r~   mimer   r   s                        r-   gemini_parts_from_bundlez"KYCEngine.gemini_parts_from_bundle  s   H%%+iiiii	7* !%		) 4 4+7 7 	2 )/&7j!9:: (	Q (	QAa&& %%,,Cc3'' syy{{ EE&MM+V2244DEE,''-2E5500LUU7^^)r0022E#!$\%6Q!7!7 # # #!"# LL|s||$||n||]b||pstypzpz||     Q Q!!T** eeEll55,,!"'7!<!<!.#66 )>:Y:Yf&Ps&P&Ph&P&PQRRR%%n&:&:&<&<==" .771::-D(..y!<<4m4-N-NOPPPP%Q( 	f>?@@@s   .E  EENFrA  doc_cfgrh  r  llm_ocr*Callable[[Dict[str, Any]], Dict[str, Any]]Optional[Dict[str, str]]debugr   r  r  c
                  9 |pi }t          j        |          }
|
t          d|           i }|D ]1}|                    |d         g                               |           2|D ]}||                             d             i }d>d
}g }i t          |
j        dd                   d}i }|j        D ]`9g }9j	        D ]+}|
                    |                    |g                      ,|s;9fd|D             }|                    d            |d |9                   }|sv9j        pd                                }|dk    rV|d         }9j	        d         t          |d                   dddd|d         dd|9j        <   |d         |d         9j        <   t!          9j        p9j        pi                     d          pd          }t'          9j        pi                     d          pd                                          }9j        |||g d}g |9j        <   d}t*          j                            |          st+          j        |           |D ]}t3          j        |d         t2          j                  }t9          ||dv rdnd          }t;          |
|          }dg}|D ]A}t=          |d                    } t?          ||||	|!          }!	 9j         d"| d"|  d#}"t*          j                             ||"          }#|!!                    d$d%          \  }$}%tE          |#d&          5 }&|&#                    tI          j%        |%                     ddd           n# 1 swxY w Y   n)# tL          $ r}'tO          d'|'            Y d}'~'nd}'~'ww xY w|d                              | t          |d                   |!d(           |9j                                     |           C|d          r|                    |           bt'          |p|j(        ptS          |j*                            }(| +                    ||()          }) ||)          pi }*tY          |*tZ                    r|*                    d*          nd}+tY          |+tZ                    si }+d?d.},d@dAd2}-|j        D ]99j        pd                                }|dk    r%9j        |vr/|+                    9j                  pi }.tY          |.tZ                    si }.|.                    d3          }/	 t!          |/          }/n# tL          $ r d}/Y nw xY w|                    9j                  pg }0|0s|/dk     s|/t=          |0          k    rd}/|0|/         }t          |                    dd4                    }1|.                    d          }2|2|.                    d-          }2|2d}2t'          |2          }2|.                    d5          }3|3< |-||2t!          9j        p9j                            d          pd          6          }3	 t          |3          }3nL# tL          $ r?  |-||2t!          9j        p9j                            d          pd          6          }3Y nw xY w| .                    |3          }3|.                    d7          }4|4%t'          |4                                          dk    r |,9||2          }4n |,9|t'          |4                    }49j	        d         |1|3|2                                |4d8|d         |/d|9j        <   |d         |d         9j        <   ||d9}5|rpddl/}6|60                    |)          }7	 |7                    d*g           D ]$}&|&                    d g           D ]}8d:|8v rd;|8d:<   %n# tL          $ r Y nw xY w|7|5d<<   |*|5d=<   |5S )Bz
        1) Uses YOLO detections + rules to crop candidates.
        2) Builds ONE bundle and calls llm_ocr(bundle).
        3) Maps response back to per-field extraction pack (same shape as before).
        NzCannot read image: r  c                J    t          |                     dd                     S Nr(  rJ   rG   r   r  s    r-   <lambda>z,KYCEngine.extract_with_llm.<locals>.<lambda>  s    E!%%2D2D,E,E+E r/   r   rM  rQ  r&   rp   c                ^    t          | j        pd          }|dk    rd}t          |d          S )NrK   r   r   )rp   rd  rS   )rM  r   s     r-   top_kz)KYCEngine.extract_with_llm.<locals>.top_k  s3    D',1--AAvvq!99r/   ru   )r  doc_image_shapec                    g | ]=}t          |                    d d                    t          j                  k    ;|>S )r(  rJ   )rG   r   rV  )rr   r   rM  s     r-   rt   z.KYCEngine.extract_with_llm.<locals>.<listcomp>  sF    iii5vs9K9K3L3LPUVZVgPhPh3h3h!3h3h3hr/   c                J    t          |                     dd                     S r  r  r  s    r-   r  z,KYCEngine.extract_with_llm.<locals>.<lambda>  s    5vs1C1C+D+D*D r/   r   rk  r   r(  rt  )r  r  ocr_conf	value_rawr   
ocr_methodrt  r   r  lengthr  r   )r   r  r]  r  r  llm_ocr_debugr  r  gffffff?gHzG?r  r  r  r   r  r  rK   wbzFailed to save debug image: )r  r  r   )r  r   r  r  r%   r   c                   |dk    rt          |          S |dk    rt          t          |                    S | j        dv rt	          |          S | j        dv r$|dk    rt          |          nt          |          S t          |          S )Nr  r  
first_name	last_name	full_name)	id_numberpassport_no)rA   r.   r3   r   r?   rD   r9   )rM  r  r   s      r-   normalize_valuez3KYCEngine.extract_with_llm.<locals>.normalize_value  s    y  ,U333v~~&|E':':;;;xCCC%e,,,x7777;y7H7H,U333NcdiNjNjj$U+++r/   r   r]  rG   c                .   |pd                                 }|sdS | dk    r(t          |          }|rt          |          |k    rdS dS | dk    rt          |          rdS dS | d	k    r-d
|v r't          |                                          dk    rdS dS dS )Nr   rJ   r  g??r  r[  g?mrz<ru   gffffff?)r+   rA   rP   r   
splitlines)r  r   r]  r   r   s        r-   heuristic_confz2KYCEngine.extract_with_llm.<locals>.heuristic_conf  s    ""$$A sy  )!,, CFFl$:$:3tv~~#A&&  4tu}}!88ALLNN 3 3q 8 84t4r/   r   rJ   r   )r]  r!  llm)r  	internalsr   z<BASE64_REMOVED_FOR_LOG>
llm_bundlellm_response_raw)rM  rQ  r&   rp   )rM  rQ  r  r%   r   r%   r&   r%   )r   )r  r%   r   r   r]  rp   r&   rG   )1r  rJ  r  
setdefaultrR   sortrN   r  rj  rS  extendr   rX  r>   rG   r   rp   r]  r   r%   r+   ospathexistsmakedirsr{  r  r|  r~  r  rP   r  r  ra   openwriter  	b64decoder   printrl  r   r   r  r   r   r  copydeepcopy):r  rA  r  r  r  r   r  r  r  r  r  by_classr   r   candidates_for_keyr  r  r  r  r  r  r  bestr]  r  
plan_entry	debug_dirdetrt  rM  variantsr   r  data_urlimg_filenameimg_pathheaderencodedr$  r  r   r  llm_resp
llm_fieldsr  r  r  cand_idxdet_listr  r   r(  	norm_textr   r(  clean_bundler   rM  s:                                                            @r-   extract_with_llmzKYCEngine.extract_with_llmg  s	   "  %2
j((;E^EEFFF46 	? 	?A,44;;A>>>> 	G 	GAQK!E!EFFFF ?A	 	 	 	 -/.0T#)TVUVTV-EXEX$Y$Y	!#M T	/ T	/D/1J& 8 8!!(,,r2"6"67777 iiiiZiiiJOO D DOEEE#LUU4[[L1J M+V2244D v~~!!}"&"21"5 %d6l 3 3 $!%"&"& L%&	$ 	$tx  04F|	'"48,
 ! $*//99 L )/R44W==CDDJJLLE x ,  J ,.tx('I7>>),, 'I&&&! = =xF2:>>>"49L1L1LRVWW(d33 &; " = =Aj677C4TaY`o{|||HB*.('B'BQ'B'B'B'B'B#%7<<	<#H#H*2..a*@*@!(D11 ?QGGF$4W$=$=>>>? ? ? ? ? ? ? ? ? ? ? ? ? ? ?$ B B B@Q@@AAAAAAAAB |,33"$)#f+$6$6*25 5   
 'tx077<<<<#=$ ,' /"":... _ 7_;Nw;_;_``11kWY1ZZ76??(b/9(D/I/ISX\\(+++t
*d++ 	J		, 		, 		, 		,	 	 	 	 	& M ;	7 ;	7DM+V2244Dv~~x111>>$(++1rDdD)) xx00Hx==    *--dh77=2H !||x3x==888$CSWWVS1122Hxx''H88G,,8}}H88L))D|%~dH3tGXG\`\l\p\pqy\z\zG~  DA  DA  B  B  BBT{{ B B B%~dH3tGXG\`\l\p\pqy\z\zG~  DA  DA  B  B  BB==&&D..I C	NN$8$8$:$:b$@$@+OD$AA		+OD$IGG	 #.q1$ %^^--'#F!)	  	 F48 ,/v;Igtx((i88 	/ KKK==00L%))(B77 M MAUU<44 M M+q002LA./MM     !-C&.C"# 
st   !AM::(M.	"M:.M22M:5M26M::
N 	N	N 	1UUUYAZ$#Z$;_ 
__extracted_fields	face_packOptional[Dict[str, Any]]c                   |pi }d |j         D             }d}t                      }g }	g }
g }i }g }g }dAd}dBd}|j         D ] }|                    |j                  }|sddddd dd||j        <   1|                     t          |                    dd                              }|t          |j                  k    }|r|                    |j                   |j        pd	                                }|dk    r d}t          j        |dz            }d }d }d }n|                     t          |                    dd          pd                    }|                    d          pd}|                    d          pd}|t          |j                  k    o-t          t          |                                                    }t          j        |t!          d|          z            }|j        r|r|dz  }d}d }|dk    rF|j        r?t'          |j        t          |pd          |j        pi           \  }}|sd}|p	|j         d } |||||          \  }} |sd}|p| }|s |                    d!|j         d"|            |	                    t          |          t          |j                  f           |                     ||          }!d }"d#}#|!r3|dk    r,|j        pd$	                                }$|$d%k    r|pd}%|!}&|d&k    rt3          |%          }%t3          |&          }&nF|j        d'v rt5          |%          }%t5          |&          }&nt7          |%          }%t7          |&          }&|%|&k    rdnd}"|"dk    rd(nd)}#n|$d*k    r<t          t9          |pd|!                    }"|"t          |j                  k    rd*nd+}#nC|$d,k    r=t=          |pd          }'t=          |!          }(|'r|(r|'|(k    rdnd}"|"dk    rd-nd.}#nd }"d/}#|"s|
                    t          |"          t          |j                  f           |j        r6|                    t          |"          t          |j                  f           |j         rJ|"dk    r!|#d0v r|                    d1|j                    |#d+k    r|                    d1|j                    |||||t          |          |"|#||d2
})|rh|)!                    |||                    d3          |                    d4          |                    d5          |                    d6          d7           |)||j        <   "|r |t!          dtE          |                    z  nd}* ||	          }+|
r ||
          nd },|r ||          nd }-d}.|j#        %tE          |          tI          |j#                  k    }.d }/d }0|K|                    d8          }0|j%        d9k    r|                    d9          }/n|                    d:          }/t          |-|-n|,pd          }1|                     d;t          |+          z  d;|1z  z             }2|2}3|j&        rC|/?|                     d<|2z  d=|                     t          |/                    z  z             }3n|2}3tO          ||*|+|,|-|.tE          |          |||/|0>          \  }4}5|4|5|*|+|,|-tE          |          |.|/|j%        |2|3d?
|d@S )CNc                     g | ]}|j         	|S rn   )rT  )rr   r   s     r-   rt   z#KYCEngine.score.<locals>.<listcomp>m  s    AAAajA!AAAr/   r   rM  rQ  r  r%   r  r   r  r&   r   c                   | j         pi }|sdS |pd}|pd}|                    d          }|                    d          }|                    d          }	|;	 t          |          }
t          |          |
k    rdd|
 fS n# t          $ r Y nw xY w|;	 t          |          }
t          |          |
k     rdd|
 fS n# t          $ r Y nw xY w|	;	 t          |	          }
t          |          |
k    rdd	|
 fS n# t          $ r Y nw xY w|                    d
          }|r8t          |          }t          |                              |          sdd| fS |                    d          }|rH	 t          j        t          |          t          |                    dS n# t          j	        $ r Y nw xY w|dk    r'|                    dd          rt          |          dS dS )NrQ  r   r  
min_length
max_lengthFlength_expected_min_length_max_length_prefixprefix_r  )Fregex_mismatchr  
must_parse)Fdate_unparsed)r   r   rp   rP   r   r%   r.  r)   	fullmatchr	  r   )rM  r  r  r  r   raw_snorm_sr  rC  rD  LrH  prefr  s                 r-   validate_constraintsz-KYCEngine.score.<locals>.validate_constraintsz  sm    &BA "!zI2EZRFUU8__F|,,J|,,J!FA6{{a''$&<&<&<<< (    D%JA6{{Q$&7A&7&777 '    D%JA6{{Q$&7A&7&777 '    D UU8__F 36{{6{{--d33 3 "2D"2"222EE'NNE |CJJF<<D66 Ex   D v~~!%%e"<"<~#E**211:sH   (B 
BB(B? ?
CC(C< <
D	D	3/F% %F76F7pairsList[Tuple[float, float]]rG   c                    | sdS t          d | D                       }|dk    r)t          t          d | D                       |z            ndS )NrJ   c              3      K   | ]	\  }}|V  
d S r   rn   )rr   r   rF  s      r-   r   z0KYCEngine.score.<locals>.wavg.<locals>.<genexpr>  s&      ))41aQ))))))r/   r   c              3  &   K   | ]\  }}||z  V  d S r   rn   )rr   r   rF  s      r-   r   z0KYCEngine.score.<locals>.wavg.<locals>.<genexpr>  s*      55tq!QU555555r/   )r   rG   )rS  sws     r-   wavgzKYCEngine.score.<locals>.wavg  sb     s))5)))))B?AAvv555u55555:;;;3Nr/   FrJ   missing)presentdet_okocr_ok
extractionr   rR  r  r   rk  TrI   r  r  r   r   g{Gz?rK   _invalidzinvalid::no_user_inputrY  exactr  r  exact_matchexact_mismatchfuzzyfuzzy_below_thresholdr  
date_matchdate_mismatchrL  )rd  rh  z	mismatch:)
r[  r\  r]  r  r  r^  r   rR  validvalid_reasonr  r  rt  r   )r  r!  r  r  rt  r   rR  r  r  r  r  gffffff?)r  coverager^  	match_all
match_corecount_gate_okdetected_countmismatch_flagsinvalid_flags
face_valueface_reason)
rk  r^  rl  rm  detected_fields_countrn  facerz  	doc_scorefinal_score)decisionreasonsscores	per_field)
rM  rQ  r  r%   r  r   r  r   r&   r   )rS  rT  r&   rG   )(rj  r`   r   r   r  rG   rV  addrX  r>   mathsqrtrW  r   r%   r+   rT   rT  r^  rs  r   rR   ra  r  rZ  rA   r?   r9   rf   r\  r   rc  rb  updaterP   rm  rp   rz  ry  decide)6r  r  r=  r  r>  r  required_rulesfound_required_detdet_ok_keysextraction_pairsmatch_pairs_allmatch_pairs_corer{  rp  rq  rR  rY  rM  r$  r  r\  r  r]  r^  r  	raw_value
norm_valuevalid_okrj  r`  rR  c_okc_reasonrN  mscoremreasonmtgot_normexp_normgotexprecrk  extraction_scorerl  rm  rn  face_valrs  
base_matchrv  rw  rx  ry  s6                                                         r-   r   zKYCEngine.scorec  sa     %2
AAW]AAA55685768$&	$&#%4	 4	 4	 4	l	O 	O 	O 	O M x	& x	&D $$TX..A $VY!Y' '	$(# }}U155S+A+A%B%BCCHt'8!9!99F *)))M+V2244Dv~~!Yx#~66
 	!

==quuZ/E/E/L)M)MNNEE+..4"	UU<006B
"eD,=&>&>>aDZI^I^I`I`DaDa!Yx#dH2E2E'EFF
} ( ("a'"HLv~~$.~*4>3z?OR;P;PRVRbRhfhii
F I$H#)#H-H-H-HL11$iTTND( 8 +7x K$$%I%I%I<%I%IJJJ##U:%6%6dk8J8J$KLLL))$
;;H&*F%G  2DFNNo3::<<==)/RH'Hy((#8#B#B#8#B#B%MMM#1(#;#;#1(#;#;#4X#>#>#4X#>#>$,$8$8SScF/5}}mmBRGG7]]"#7	R#R#RSSF)/59M3N3N)N)NggTkGG6\\*9?;;C*844C 2s 2(+s

28C--,,_!%"1!&&fuT[7I7I'JKKK? Q$++U6]]E$+<N<N,OPPP BS==W0S%S%S"))*@dh*@*@AAA555"))*@dh*@*@AAA "  $$#J//!! ,# #C  

$","#%%"5"5"#%%"5"5EE&MM%&UU?%;%;     #&IdhIW`&QN0C0C)D)DDD]`4 011.=GTT/***4	0@Jdd+,,,d
,8 --W5V1W1WWM #--11K"h..$==22$==33)?::iFVSVXX
MM#.>(?(?"?#
BR"RSS	  % 	(#"mmD9,<tdmmTYZbTcTcFdFd?d,dee'"'!'{++)'#
 
 
' !$.&(),[)9)9!. &2&*  #
 
 	
r/   )r  r%   )rs   rG   r&   rG   )rM  rQ  r  r  r&   r   )r  r%   r(  rG   r  rG   r  r%   r  rp   r&   r  )r  r  r   r%   r&   r   )r  r   r&   r  )rA  r%   r  rh  r  r  r  r  r   r   r  r  r  r   r  rp   r  rp   r&   r   )r  rh  r=  r   r  r  r>  r?  r  r   r&   r   )r  r  r  r   r<  r  r  r  r  r  r  r  r  _OCR_MAX_DIMr<  r   rn   r/   r-   r  r    sR       	, 	, 	, 	, , , , \,	 	 	 	; ; ; ;~   $, , , , , \,\ 8 8 8 \8t 9 9 9 \9v K K K \Kf *.37 1'x x x x xxH
 H
 H
 H
 H
 H
r/   r  r  rk  r^  rl  rm  rn  ro  rp  rR  rq  rr  rs  Tuple[str, List[str]]c                R   g }| j         ;|s9|                    d           |                    d| d| j                     d|fS || j        k     r|                    d           d|fS |r!|                    |d d                    d|fS |r!|                    |d d                    d|fS | j        r|	|                    d|
pd	            d|fS t          |	          t          | j                  k     r;|                    d
           |                    d|	dd| j        d           d|fS || j        k    r|| j        k    ru|0|| j        | j	        z   k    rd|fS |                    d           d|fS || j
        k    rd|fS |                    d           ||                    d|d           d|fS || j        k    rD|| j        k     r|                    d           || j        k     r|                    d           d|fS |                    d           d|fS )Ndetected_fields_count_too_lowz	detected=z_min=REJECTcoverage_too_lowrw   REVIEWzface_required_but_unavailable:unknownface_below_thresholdzface=z.3fz_thr=APPROVEno_core_user_inputcore_match_below_thresholdz
match_all=extraction_lowpartial_coveragecoverage_insufficient)rm  rR   rv  r  ry  rG   r{  rn  rp  rx  rr  ru  )r  rk  r^  rl  rm  rn  ro  rp  rq  rr  rs  ry  s               r-   r  r  r  s    G(4]46777[>[[8Y[[\\\  '///)***    !}SbS)***   !~crc*+++   ! %NNVK<T9VVWWWW$$uW%ABBBBNN1222NNZ:ZZZ8TZZZ[[[W$$ 7///J'B`4`4`g<w?ddee '))NN/000W$$777g%%3444 NN7	777888   7...666NN+,,,g222NN-...  NN*+++Wr/   r  r  r   c                   || v r|| v r| |         | |         c| |<   | |<   |                     di           }||v r||v r||         ||         c||<   ||<   |                    dg                               |||d           d S )Nr  swap_events)rE   rF   rR  )r   r  rR   )r  r  rE   rF   rR  r  s         r-   swap_field_keysr    s    F{{qF{{%ay&)q	6!9gr""AAvv!q&&qT1Q4
!ad++22f3U3UVVVVVr/   cfgc                H    |j         D ]\  }}t          | |||d           d S )Nconfig_swap_pairsrT  )r|  r  )r  r  r  rE   rF   s        r-   apply_config_swapsr    sB     M M1	1a8KLLLLLM Mr/   r  ocr_first_rawocr_last_raw	exp_firstexp_lastmarginc                    t          | pd|pd          }t          |pd|pd          }||z   dz  }t          | pd|pd          }t          |pd|pd          }	||	z   dz  }
|
||z   k    r
d||
||	ddS d||
||ddS )Nr   r6  T)zfirst->lastzlast->first)swapscore_normalscore_swappedr  F)zfirst->firstz
last->last)rf   )r  r  r  r  r  s11s22normals12s21swappeds              r-   best_name_assignmentr    s     }2IO
D
DC
|1r8>r
B
BCCi3F
}2HN
C
CC
|1r9?
C
CCSyCG&  fwkn  @C  \D  \D  E  E  	E6Gil|  YA  YA  B  B  Br/   r  r  c                   |j         sd S d| vsd| vrd S |                    d          pd}|                    d          pd}|r|sd S | d                             d          pd}| d                             d          pd}t          |||||j                  }|                    d          rDt	          | |ddd           |                    d	g                               ddd|d
           d S d S )Nr  r  r   r  )r  r  heuristic_name_swaprT  r  )rE   rF   rR  meta)r}  r   r  r~  r  r  rR   )	r  r  r  r  r  r  f_rawl_rawinfos	            r-   apply_name_swap_if_neededr    sN    6!![%>%>|,,2I~~k**0bH H < $$[117RE;##K006BEui#J^___Dxx M	<Mbcccc]B//66\P[g|  GK  8L  8L  	M  	M  	M  	M  	MM Mr/   rU  r_      TF)doc_config_payload
doc_configr  r  rB  	yolo_confyolo_iour  r  cache_enginer  ri  r  r?  r  Optional[DocConfig]r  4Optional[Callable[[Dict[str, Any]], Dict[str, Any]]]r  r  r  r  r  r  r  c                   |t          |          }n||}nt          d          |t          }|rt          |           nt	          |           }|                    |||	|
|          }|                    ||||||          }|d         }|d         }t          |||           t          ||||pi            d }|j	        rR|rI|d         
                    d          p|d         
                    d          }t          |||	          }nd d d
d d}|                    |||||          }||d         |d         |d         |d         d}|j	        rdnd}g }|j	        r|s|                    d           |d         dk    r|                    d           ||d<   ||d<   |rD||d<   ||d<   ||d<   ||d<   |
                    d          |d<   |
                    d          |d<   |S )NzBdoc_config_payload or doc_config must be provided (server-driven).)r(  r  r  r  )r  r  r  r  r  r  	doc_photophoto)rA  rB  rC  rF  r
  )r  r>  r  rx  rz  ry  r{  )ri  rx  rz  ry  r{  rT  disabledupload_selfie_requiredr  retake_document_photoselfie_mode
next_steps
detectionsru  r  r  )r  r   r)  r  r  r  r<  r  r  ry  r   rO  r   rR   )r  rA  ri  r  r  r  r  rB  r  r  r  r  r  r  r  enginer  packr  r  r>  rt  scoringresultr  r  s                             r-   run_kycr    s   , %%&899		]^^^'3NZ
###:9N9NF==iXf^e=ffD"" #  D (^F[!Ivy#...fij6FBGGG I
 g 	gW%))+66Y)G:L:P:PQX:Y:YD)[l  ~B  C  C  CII$(DDTaeffIll3:Z_l``G J'(#9%[) F !$ 6F**JKJ
 4&7 42333jX%%1222'F=%F< B#|!x'{"v#xx55|%)XX.@%A%A!"Mr/   )r$   r%   r&   r%   )rE   r%   rF   r%   r&   rG   )r$   r%   r&   rg   )r   r%   r&   r   )r   r%   r   r   r&   r   )r   r%   r&   r%   )r&   r%   )r   r%   r&   r%   )r   r   r   r   r&   r   )r   r   r&   r   )r   r   r&   r   )r   r   r&   r   )r   r%   r   r%   r   r%   r&   r   )r  r   r&   r   )r   r   r&   r*  )r   r%   r>  r?  r;  rp   r<  r@  r=  r%   r&   r   )r&   r   )rR  r%   r&   r   )ro  r   r   r%   r   r   r&   r   )rt  ru  rv  rG   r&   ru  )r  ru  r&   ru  )r  ru  rF  rp   r  rp   r&   ru  )r  ru  rt  ru  r&   ru  )r  ru  r&   ru  )r  ru  r&   ru  )rv   )r  ru  r  rp   r&   ru  )
r  ru  r  rp   r  rp   r  rp   r&   ru  )r  )r   rp   r  rp   r&   rp   )r  ru  r  r%   r  r%   r  rp   r  rp   r&   r%   )r&   r  )rA  r%   rB  r%   rC  rD  r&   r   )r  r   r&   rh  )r  r%   r&   r  )r  rh  rk  rG   r^  rG   rl  r   rm  r   rn  r   ro  rp   rp  rR  rq  rR  rr  r   rs  r   r&   r  )r  r   r  r   rE   r%   rF   r%   rR  r%   r&   r   )r  r   r  r   r  rh  r&   r   )r  )r  r%   r  r%   r  r%   r  r%   r  rG   r&   r   )
r  r   r  r   r  rh  r  r  r&   r   )r  r%   rA  r%   ri  r%   r  r?  r  r  r  r  r  r  rB  r   r  rG   r  rG   r  r%   r  rp   r  r   r  r   r&   r   )t
__future__r   r   r)   r}  r  r  loggingr  dataclassesr   r   typingr   r   r   r	   r
   r   r   r  r  r{  r  ultralyticsr   r%   	maketransr2   r5   r  r8   r7   rp   getenvr  r  r  r  r   rG   r  r+   r>   r  	getLoggerr  r"  r.   r3   r9   r?   rA   rD   r]   rf   r   r   r   r   r   r   r   r   r   r   r  r)  r   ValidatorFnr1  r9  rO  r   r   rX  r[  ra  rc  rf  rn  rf  rs  r~  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r=  r  r?  r@  rO  rQ  rh  r  r  r  r  r  r  r  r  r  r  r  rn   r/   r-   <module>r     s	   # " " " " " " 				 				         ( ( ( ( ( ( ( ( G G G G G G G G G G G G G G G G G G 



            mm %#%S%%%)3%04c%#%S%%%)3%04c% 	#% S% % &*3% 15c% 	#	% S	% !$3c	% % %    











       NOOs929.7788C		"8$??@@ bi(*STT  BeIBI&>EEFF !*+NPS!T!T!Z!Z!\!\!b!b!d!d i "  w""2 2 2 24 4 4 4   & & & &$ $ $ $
2 2 2 2. . . .(
3 
3 
3 
3   <   &- - - -^            $             (+ + + +BA" A" A" A"H T#s(^,eD(3-4G.HHI! ! ! !H* * * *6 %)'6- 6- 6- 6- 6- 6-r      1 1 1 1# # # #$ $ $ $2   , , , ,
 6-;/ /     3 3 3 3&) ) ) )
8 8 8 8   P P P P*4 4 4 44 4 4 484 4 4 4   C C C C C 9=1[\      ,          #- +  +  +  +  +  +L$ $ $ $ $ $ $ $
g g g g g_ g g g
(b (b (b (b (b (b (b (bV ,0 / / / /Y^
	 	 	 	( ( ( (< . . . . . . . .< # # # # # # # #@: : : :B )+ * * * *y~" " " "R
 R
 R
 R
 R
 R
 R
 R
rD D D DVW W W WM M M M B B B B B(M M M M8 8<*. IM/3+/!)X X X X X X X Xr/   