o
    էjDm                  	   @  s  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m	Z	 d dl
mZmZmZmZmZmZ dd	d
ZddddZdddZddd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/d0d1d0d2d3d4d3d5d6i d7d6d8d9d:d9d;d<d=d<d>d?d@dAdBdCdDdCdEdFdGdHdIdHdJdKdLdKdMdNdOdNZh dPZddSdTZddWdXZddYdZZdd]d^ZddadbZddcddjdkZddmdnZe	G dodp dpZddrdsZddwdxZ dddyddzd{d|d}d~dddZ!dS )    )annotationsN)	dataclass)AnyDictListOptionalTupleCallablepayloadDict[str, Any]returnOptional[str]c                 C  s   |  d}t|tr?| d}t|tr| d}|rt|S | d}t|tr?|r?t|d tr?|d  d}|r?t|S |  d}t|trT| d}|rTt|S dS )z
    Your PHP output:
    {
      "status":"success",
      "data":{
        "config":{"yolo_model_path":"C:\\...\\best.pt"},
        "rules":[{"yolo_model_path":"C:\\...\\best.pt", ...}]
      }
    }
    dataconfigyolo_model_pathrulesr   N)get
isinstancedictstrlist)r
   r   cfgpr    r   $/var/www/html/policy_provider_llm.py_extract_yolo_model_path   s$   








r         (@   urlr   params	timeout_sfloatretriesintc                 C  s  dd l }dd l}dd l}|jdd | D }| d| v r dnd | }dddd	d
dd}	|jd}
|
r=d|
 |	d< d }t|d D ]}z|j	j
||	dd}|j	j||d,}t|dd pd| }| }|jdd}|jddpxd }| }W d    n1 sw   Y  |dkrz||}W n	 ty   Y nw z|d}W n ty   |jddd}Y nw |pd }|std| d| d| d |d!rd"|d d#  v sd$|d d%  v r|d d& d'd(}td)| d| d| d*| z	t|W W   S  tjy8 } z|d d& d'd(}td+| d| d| d*| |d }~ww  |jjy } zm|}z+| }z	|jddd}W n tya   t|}Y nw |pfdd d& d'd(}W n ty|   d}Y nw |j d,krtd-| d*| |||k rt!"d.d/|   W Y d }~qEtd0| d1| d*| |d }~w ty } z!|}||k rt!"d.d/|   W Y d }~qEtd2| d1| |d }~ww td2| d1| )3Nr   c                 S  s   i | ]\}}|d ur||qS Nr   ).0kvr   r   r   
<dictcomp>8   s    z"_http_get_json.<locals>.<dictcomp>?&zoMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36z!application/json, text/plain, */*zen-US,en;q=0.9,fa;q=0.8zhttps://chat.fxtrendo.com/closezgzip, deflate)z
User-AgentAcceptzAccept-LanguageReferer
ConnectionzAccept-EncodingKYC_RULES_TOKENzBearer Authorization   GET)headersmethod)timeoutstatuszContent-Type zContent-Encodinggzipzutf-8zlatin-1replace)errorszEmpty response body (status=z, url=z, ctype=)<z<html   
cloudflarei  iX  
z\nz7Non-JSON response (likely WAF/Cloudflare HTML). status=z
, preview=zJSON decode failed. status=i  z&403 Forbidden from policy server. url=g?r   zHTTP error from z: zFailed to fetch JSON from )#osr8   urllib.errorparse	urlencodeitemsenvironr   rangerequestRequesturlopengetattrgetcodegeturlr3   lowerread
decompress	ExceptiondecodeUnicodeDecodeErrorlstripRuntimeError
startswithr9   jsonloadsJSONDecodeErrorerror	HTTPErrorr   codetimesleep)r   r   r    r"   r@   r8   urllibqsfullr3   tokenlast_errireqrespr6   	final_urlctypeencrawtexttpreviewjeebody	body_textr   r   r   _http_get_json3   s   
6

rq   r'   r   c                 C  s   | d u rd S t | ttfr| S t | trB|  }|sd S |dr&|ds0|drB|drBzt|W S  t	yA   |  Y S w | S )N{}[])
r   r   r   r   striprU   endswithrV   rW   rP   )r'   sr   r   r   _coerce_json   s   
(ry   List[Dict[str, Any]]c                 C  s  t | dtrt | d dtr| d d S t | dtr$| d S t | dtrG| d }|rGt |d trGd|d v sEd|d v rG|S |  D ]5\}}t |trbt |dtrb|d   S t |tr|rt |d trd|d v s|d|d v r|  S qKg S )Nr   r   r   required_stepsvalidation_logic)r   r   r   r   rD   )r
   lst_r'   r   r   r   _find_rules_list   s    $*r   Document_Bodydocument_bodyzDocument BodyPhoto	doc_photoz	Doc PhotozDoc Logodoc_logoDoc_Logoz	Full Name	full_name	Full_Namez
First Name
first_name
First_Namez	Last Name	last_name	Last_Namez	ID Number	id_number	ID_NumberzPassport Nopassport_noPassport_Noz
Birth Date
birth_date
Birth_DatezExpiry Dateexpiry_dateExpiry_Datez
Issue Date
issue_date
Issue_DateGendergenderNationalitynationalityzMRZ ZonemrzMRZ_ZoneAddressaddress
QR_Barcode
qr_barcodez
QR BarcodeIssuing_Authorityissuing_authorityzIssuing AuthorityPlace_of_Birthplace_of_birthzPlace of Birth>   r   r   r   r   r   
class_namelogicc                 C  s   | dp| dpd  }|dv r|S | pd }d|v r"dS d|v r(dS d|v s5d|v s5|d	r7d
S d|v s?d|v rAdS d|v sQd|v sQd|v sQ|dkrSdS dS )Ntypeocr_kindr7   >   r   datenonerj   numericr   r   numberz nonor   qrbarcoder   photologor   rj   )r   rv   rM   rw   )r   r   rk   cnr   r   r   _infer_ocr_kind   s     r   keyTuple[str, bool, bool, float]c                 C  s   | dv rdS | dv rdS dS )N>   r   r   )exactTTg@>   r   r   r   )fuzzyTTg?)optionalFF      ?r   )r   r   r   r   _default_match_for_key  s
   r   c                 C  s&   i }dD ]}|| v r| | ||< q|S )N)length
min_length
max_lengthprefixregex
must_parsereject_incomplete_namer   )r   cr&   r   r   r   _constraints_from_logic  s   r   xOptional[bool]c                 C  sb   | d u rd S t | tr| S t | ttfrt| S t | tr/|   }|dv r)dS |dv r/dS d S )N>   1yonyestrueenabledrequiredT>   0nr   offr   falsedisabledr   F)r   boolr#   r!   r   rv   rM   )r   rx   r   r   r   _to_bool  s   

r   variantr|   c                 C  s   i }dD ]}t | |}t|tr|| qdD ]}t|tr(t ||nd}t|tr4|| qd| v rLd|vrLd|vrLd|vrL| d|d< d| v r[d	|vr[| d|d	< d
| v rjd|vrj| d
|d< |S )a  
    New rule: Face is either ENABLED (required) or DISABLED. No optional mode.

    Reads in order:
      1) validation_logic['__face__' or 'face_match' or '_face' or 'selfie']
      2) variant['face_match' or 'face_policy' or 'selfie_policy' ...]
      3) flat keys on variant
    )
face_matchface_policyselfieselfie_policy)__face__r   _facer   Nrequire_face_matchr   moder   face_match_threshold	thresholdface_metricmetric)ry   r   r   r   update)r   r|   outr&   r'   r   r   r   _extract_face_policy_on_off%  s$   	



 r   )defaultsclass_id_to_nameDict[int, str]doc_typecountryr   Optional[Dict[str, Any]]c           )      C  s  |pi }t | dpg }t|trt |pg }t|tsg }dd |D }t | dp.i }t|tr:t |p9i }t|tsAi }t| D ]2\}}	zt|}
W n	 tyZ   Y qGw t|	tsaqGt	|	dpp|	dpp|	d}|ry|
|
 qGt|}| D ]}z	|
t| W q ty   Y qw g }t|D ] }||}|sqt||  dd	}|t|p||pi }t|tsi }t||}t|\}}}}t|d
|}t|d|}t|d|}t|d|}t|d|d||v rdnd}t|d|dd}t|}t|d|d|ddp,d}|d}| dkrI|dkrI|pCd}|pHd}t|d|d|tv rWdndp]d}|d}t|trm|g}|d urzt|tszd }|i d|gd |dt||v d|d|d!|d
|d"t|d"|d"d#d|d|d$|d|d|d|d|d| q| d%p| d&p| d'}| d(p|  d	|  d	| } |d)}!|!d u r|rt|nd }!t| |}"t|"d*pd+  }#t	|"d,p|"dp|"d-}$t|d-d.}%|#d/v r-d0}%n|#d1v r5d.}%n	|$d ur>t|$}%t|"d2pJ|d3d4}&t|"d5|d6d7}'i d8|  d	|  d	|  d'|d(| d9|d:|d;|d)|!d<t|d<d=d>t|d>d?d@t|d@dAdBt|dBdCdDt|dDd7dEt|dEdFdGt|dGdHd-t|%d3t|&d6t|'|dIg t|dJd0t|dKdLdM}(|(S )NNr{   c                 S  s    h | ]}t | rt|qS r   )r   isdigitr#   )r%   r   r   r   r   	<setcomp>V  s     z8build_doc_config_payload_from_variant.<locals>.<setcomp>r|   r   require
must_exist r~   
match_type
match_gate
must_matchweightmin_det_confgffffff?      ?min_ocr_conf皙?expected_lenr   r   	validatorIRr   iran_national_code
   max_candidatesr   r1   input_aliasesclass_namesr   r   match_thresholdg333333?constraintsidrule_id
variant_idvariant_namemin_detected_fields_countr   r7   r   r   F>   r   r   r   r   	mandatoryT>   r   r   r   r   r   r   score01r   r   g      ?doc_idr   r   r   approve_min_coverager   approve_min_extractiong(\?approve_min_match_coregq=
ףp?approve_min_match_all        review_min_coveragereject_below_coverageg      ?approve_no_input_extra_bufferg?
swap_pairsenable_name_swapname_swap_margingQ?)r  r  r  )ry   r   r   r   r   r   rD   r#   rP   r   addsetkeyssorted_CANON_KEYSrv   rM   r9   r   r   r   r!   r   upper
_CORE_KEYSappendlenr   ))r   r   r   r   r   r{   required_idsr|   _cid_k_logic_cidrd   idsr&   r   cidr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  	min_countr   r   enabled_flagr   r   r   r
   r   r   r   %build_doc_config_payload_from_variantG  s,  




"



&&


,

"(


&




r$  payloadsc              
   C  s  i }| D ]v}| dpg D ]l}t|tsqt| dpd}|s!q||vr2t|}d|d< |||< q|| }ttt| ddt| dd|d< ttt| dd	t| dd	|d< ttt| d
dpjdt| d
dpsd|d
< qqdt|	 ddd}|S )am  
    Creates a "union" payload to run ONE extraction (YOLO crops + LLM OCR) for all fields.
    For each key, we keep:
      - min_det_conf: minimum across variants (to not miss)
      - min_ocr_conf: minimum across variants
      - max_candidates: maximum across variants
      - weight/match settings don't matter for extraction, but kept from the first rule
    r   r   r7   Fr   r   r   r   r   r   r1   union_extractionN)r  r   r  r   )
r   r   r   r   r!   minr#   maxr   values)r%  by_keyr   rr   rrcurunion_payloadr   r   r   _merge_union_payload  s.   	

**4
r/  c                   @  s>   e Zd ZU ded< ded< ded< ded< ded< d	ed
< dS )VariantCandidater   r  payload_doc_iddecisionr   scoresz	List[str]reasonsr   sort_keyN)__name__
__module____qualname____annotations__r   r   r   r   r0    s   
 r0  dc                 C  s4   | pd  } | dkrdS | dkrdS | dkrdS dS )	Nr7   APPROVE   REVIEWr   REJECTr1   r   )r  )r:  r   r   r   _decision_rank   s   r?  r2  r3  r   c           	   	   C  s   |pi }| d}| d}| dd}| dd}| d}| d}t| t|d ur.|ndt|d ur7|ndt|d ur@|ndt|t|t|d urQ|fS dfS )	N
match_core	match_all
extractionr  coveragefacefinal_scoreg      )r   r?  r!   )	r2  r3  rx   mcmaexcovrD  rE  r   r   r   _candidate_sort_key+  s"   



rJ  Fr   r   g333333?r=   )selfie_image_path
user_inputdebugr   device	yolo_confyolo_ioumax_detphp_endpoint_url
model_pathdoc_image_pathllm_ocr*Callable[[Dict[str, Any]], Dict[str, Any]]rK  rL  Optional[Dict[str, str]]rM  r   rN  rO  rP  rQ  c           2      C  s  |
pi }
|pi }t |||dt|
ddt|
ddd}t|dd d	krCd d d
i d|d gi d}|	rA||d< |S t|}|rK|}|s_d d d
i dgi d}|	r]||d< |S |	rktd| td| | |}t	|j
}t|}|sd d d
i dgi d}|	r||d< |S g }g }g }|D ]#}t|t	sqt|||||
d}|| || | || q|sd d d
i dgi d}|	r||d< |S |j|||||d}t|}| |}|j||||d |	d}|d }|d }d } tdd |D }!|!r'|r |di dp|di d}"| j|||"d} nd d dd d } d }#d }$d }%g }&t|D ]\}'}(||' }||' })t|}*t|}+| |*|+|( | |*|+|(| |(jr^| nd },|j|(|*||,|	d!}-t|-d"prd}.|-d#p{i }/t|.|/}0t|d$|.|/|-d%pg |-d&pi |(jrd'nd(|(jr|sd)gng d*}1|&tt|d+t|d$|.|/|1d%pg |0d, |#d u s|0|#kr|0}#|1}$|)}%q3|$d u rd d d
i dgi d}|	r||d< |S t|$d$t|$d$|$d"|$d#|$d%|$d&|$d-|$d.d/}|	rSd0d1 t|&d2d3 d4d5D |d6< |%|d7< ||d< ||d8< ||d< ||d< | |d9< |d:|d:< |d;|d;< |S )<N)r   r   policy_timeout_sr   policy_retriesr   )r   r    r"   r6   r7   successr=  zpolicy_fetch_failed:message)selected_variant_doc_idselected_variantr2  r3  r4  	per_field
raw_policy!yolo_model_path_missing_in_policyz#[KYC] yolo_model_path(from server):z[KYC] yolo_model_path(used):no_variants_found)r   r   r   r   r   no_variant_executed)confiourN  rQ  )rU  rL  rM  fields	internalsc                 s  s    | ]	}t |d dV  qdS )r   FN)rJ   )r%   r   r   r   r   	<genexpr>  s    z'run_kyc_auto_variant.<locals>.<genexpr>quadsr   r   )rT  rK  doc_photo_quadselfie_missing)r  cosinereasondetails)rL  	face_packrM  r2  r3  r  r4  r^  r   r   upload_selfie_required)r  r2  r3  r4  r^  selfie_mode
next_stepsr  )r  r1  r2  r3  r4  r5  rp  rq  )r\  r]  r2  r3  r4  r^  rp  rq  c                 S  s   g | ]}|j qS r   )__dict__)r%   r   r   r   r   
<listcomp>  s    z(run_kyc_auto_variant.<locals>.<listcomp>c                 S  s   | j S r$   )r5  )r   r   r   r   <lambda>  s    z&run_kyc_auto_variant.<locals>.<lambda>T)r   reversevariant_candidatesbest_variant_raw_rule
detectionsrD  
llm_bundlellm_response_raw)rq   r!   r   r#   r   rM   r   print
get_enginer   r   r   r   r$  r  doc_config_from_payloaddetectr/  extract_with_llmanycompute_face_pack	enumeratecopydeepcopyapply_config_swapsapply_name_swap_if_neededr   scorerJ  r0  r  )2kyc_engine_modulerR  rS  r   r   rT  rU  rK  rL  rM  r   rN  rO  rP  rQ  r
   r   server_model_pathenginer   variantsvariant_payloadsvariant_cfgsraw_variantsr'   doc_cfg_payloaddetsr.  	union_cfgpackbase_fieldsbase_internalsface_pack_globalany_face_requiredquadbest_keybest_resultbest_variant_raw	cand_listidxr   vrawre  rf  rn  scoringr2  r3  skresr   r   r   run_kyc_auto_variant>  sR  






	&



	
 r  )r
   r   r   r   )r   r   )
r   r   r   r   r    r!   r"   r#   r   r   )r'   r   r   r   )r
   r   r   rz   )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%  rz   r   r   )r:  r   r   r#   )r2  r   r3  r   r   r   )rR  r   rS  r   r   r   r   r   rT  r   rU  rV  rK  r   rL  rW  rM  r   r   r   rN  r   rO  r!   rP  r!   rQ  r#   r   r   )"
__future__r   rV   r\   r  urllib.parser^   urllib.requestdataclassesr   typingr   r   r   r   r   r	   r   rq   ry   r   r  r  r   r   r   r   r   r$  r/  r0  r?  rJ  r  r   r   r   r   <module>   s    
"
b
	 !#$&'),-./014




( 
)'
	
