
    ^h:                         S SK r S SKrS SKrS SKJrJrJrJrJr  S SK	r	S SK
Jr  S SKrS SKJr  S SKJr  S SKJr  S SKJr  S SKJr  S S	KJr  S S
KJrJrJrJr   " S S\5      r " S S\5      rg)    N)AnyListOptionalTupleUnion)	BaseModel)
get_secret)verbose_proxy_logger)	DualCache)CustomGuardrail)UserAPIKeyAuth)GuardrailEventHooks)EmbeddingResponseImageResponseModelResponseStreamingChoicesc                   .    \ rS rSr% SrSr\\   \S'   Sr	g)PresidioPerRequestConfig"   z<
presdio params that can be controlled per request, api key
Nlanguage )
__name__
__module____qualname____firstlineno____doc__r   r   str__annotations____static_attributes__r       k/home/james-whalen/.local/lib/python3.13/site-packages/litellm/proxy/guardrails/guardrail_hooks/presidio.pyr   r   "   s     #Hhsm"r    r   c                   n  ^  \ rS rSrSrSr       SS\S\\   S\\	   S\\	   S\\   S\\	   S	\\   4U 4S
 jjjr
  SS\\	   S\\	   4S jjrS\	S\S\\   S\	4S jrS\S\S\S\	4S jrS\S\S\	S\\\4   4S jrS\S\S\	S\\\4   4S jrS\S\S\\\\4   4S jrS\S\\   4S jrS rSrU =r$ ) _OPTIONAL_PresidioPIIMasking*   Nmock_testingmock_redacted_textpresidio_analyzer_api_basepresidio_anonymizer_api_baseoutput_parse_piipresidio_ad_hoc_recognizerslogging_onlyc                 @  > USL a  SU l         [        R                   US'   [        TU ]  " S
0 UD6  0 U l        X l        U=(       d    SU l        USL a  g Un	U	b1   [        U	S5       n
[        R                  " U
5      U l
        S S S 5        U R                  UUS	9  g ! , (       d  f       N= f! [         a    [        SU	 35      e[        R                   a  n[        S[        U5       SU	 35      eS nAf[         a  n[        S[        U5       SU	 35      eS nAff = f)NT
event_hookFrzFile not found. file_path=zError decoding JSON file: z, file_path=zAn error occurred: )r'   r(   r   )r+   r   super__init__
pii_tokensr&   r)   openjsonloadad_hoc_recognizersFileNotFoundError	ExceptionJSONDecodeErrorr   validate_environment)selfr%   r&   r'   r(   r)   r*   r+   kwargsr5   filee	__class__s               r!   r0   %_OPTIONAL_PresidioPIIMasking.__init__/   s:    4 $D#6#C#CF< "6" 	 #5 0 9E48),c2d.2iioD+ 3 	!!'A)E 	" 	
 32$ S"<=O<P QRR'' 0QEWDXY   )#a&>P=QR sB   B, &BB, 
B)%B, )B, ,+DC11D>DDc                 
   U=(       d    [        SS 5      U l        U=(       d    [        R                   " SS 5      U l        U R                  c  [	        S5      eU R                  R                  S5      (       d  U =R                  S-  sl        U R                  R                  S5      (       d4  U R                  R                  S5      (       d  SU R                  -   U l        U R                  c  [	        S5      eU R                  R                  S5      (       d  U =R                  S-  sl        U R                  R                  S5      (       d6  U R                  R                  S5      (       d  SU R                  -   U l        g g g )NPRESIDIO_ANALYZER_API_BASEPRESIDIO_ANONYMIZER_API_BASEz5Missing `PRESIDIO_ANALYZER_API_BASE` from environment/zhttp://zhttps://z7Missing `PRESIDIO_ANONYMIZER_API_BASE` from environment)r	   r'   litellmr(   r7   endswith
startswith)r:   r'   r(   s      r!   r9   1_OPTIONAL_PresidioPIIMasking.validate_environmentZ   s]    'X*5QSW*X 	'
 ) 
G,>,>*D-
 	) **2STT..77<<++s2+++66yAA..99*EE D;;; + ,,4UVV0099#>>--4---88CC00;;JGG D=== - H Dr    textpresidio_configreturnc                   #     [         R                  " 5        ISh  vN nU R                  b  U R                  nGOU R                   S3nUSS.nU(       a   UR                  (       a  UR                  US'   U R
                  b  U R
                  US'   Sn[        R                  " SUU5        UR                  XgS9 ISh  vN nUR                  5       I Sh  vN n	SSS5      ISh  vN   U R                   S	3n
[        R                  " S
U
5        UW	S.nUR                  XS9 ISh  vN nUR                  5       I Sh  vN nSSS5      ISh  vN   UnUb  [        R                  " SU5        US    Ho  nUS   nUS   nUS   nUS   S:X  aG  USL aB  UU R                  ;   a!  U[        [        R                  " 5       5      -   nUX U R                  U'   USU U-   XS -   nMq     US   sSSS5      ISh  vN   $ [        SU 35      e GN GNS GN> GN1! , ISh  vN  (       d  f       GNG= f GN N N! , ISh  vN  (       d  f       N= f N[! , ISh  vN  (       d  f       g= f! [         a  nUeSnAff = f7f)z?
[TODO] make this more performant for high-throughput scenario
Nanalyzeen)rH   r   r   r5   z&Making request to: %s with payload: %s)r3   	anonymizezMaking request to: %s)rH   analyzer_resultszredacted_text: %sitemsstartendrH   operatorreplaceTzInvalid anonymizer response: )aiohttpClientSessionr&   r'   r   r5   r
   debugpostr3   r(   r1   r   uuiduuid4r7   )r:   rH   r)   rI   sessionredacted_textanalyze_urlanalyze_payloadresponseanalyze_resultsanonymize_urlanonymize_payloadnew_textitemrQ   rR   replacementr=   s                     r!   	check_pii&_OPTIONAL_PresidioPIIMasking.check_pii   s    >	,,..'**6$($;$;M &*%D%D$EW"MK/3&FO&?+C+C6E6N6N
3..:@D@W@W(<= %)M(..@#'
  '||#  ,    !08*?	    (,'H'H&I$SM(../FV $,;)%
  '||%  ,    !.6mmo(=   
   ,(../BMR -g 6 $W"5k&*6l
+y8=MQU=U  +doo=.9C

<M.M;C %<DOOK8 $,FU#3k#AHTN#R !7 )0s /..v $&CM?$STTw /(  +@	         )>       E /...x  	G	s0  J	I5 HI5 B IHIHHHI)H*AI,H8-I0H?H;H?	IH=BI1I5 =I>I5 J	II5 IHIH5	#H&$H5	0	I;H?=I?I	II	II5 I2!I$"I2.I5 1J	2I5 5
J?JJJ	user_api_key_dictcachedata	call_typec           	      N  #     UR                  SS5      n[        R                  " SU5        U R                  U5      nUS:X  a  US   n/ nU HJ  n	[	        U	S   [
        5      (       d  M  UR                  U R                  U	S   U R                  US95        ML     [        R                  " U6 I Sh  vN n
[        U
5       H(  u  p[	        X{   S   [
        5      (       d  M!  UX{   S'   M*     [        R                  " SUS    35        XsS'   U$  N]! [         a  nUeSnAff = f7f)	aI  
- Check if request turned off pii
    - Check if user allowed to turn off pii (key permissions -> 'allow_pii_controls')

- Take the request data
- Call /analyze -> get the results
- Call /anonymize w/ the analyze results -> get the redacted text

For multiple messages in /chat/completions, we'll need to call them in parallel.
content_safetyNzcontent_safety: %s
completionmessagescontentrH   r)   rI   ,Presidio PII Masking: Redacted pii message: )getr
   rW   'get_presidio_settings_from_request_data
isinstancer   appendrf   r)   asynciogather	enumerateinfor7   )r:   rh   ri   rj   rk   rm   rI   ro   tasksm	responsesindexr.   r=   s                 r!   async_pre_call_hook0_OPTIONAL_PresidioPIIMasking.async_pre_call_hook   s4    $	!XX&6=N &&';^L"JJ4POL(
+!A!!I,44 NN%&y\151F1F0? +  " #*..%"88	 )) 4HE!(/)"<cBB  !% !5
 %))B4
CSBTU $,Z K 9  	G	sH   D%A"D *AD 1D2,D ",D D%D 
D"DD""D%r;   resultc                   ^ ^^^ SSK Jn  UUUU 4S jn [        R                  " 5       nU" SS9 nUR	                  U5      nUR                  5       sS S S 5        $ ! , (       d  f       g = f! [         a
    U" 5       s $ f = f)Nr   )ThreadPoolExecutorc                  >  > [         R                  " 5       n  [         R                  " U 5        U R                  TR	                  TTTS95      U R                  5         [         R                  " S5        $ ! U R                  5         [         R                  " S5        f = f)z9Run the coroutine in a new event loop within this thread.)r;   r   rk   N)rw   new_event_loopset_event_looprun_until_completeasync_logging_hookclose)new_looprk   r;   r   r:   s    r!   run_in_new_loopB_OPTIONAL_PresidioPIIMasking.logging_hook.<locals>.run_in_new_loop  s    --/H	-&&x022++%f	 ,   &&t,  &&t,s   5A4 4(B   )max_workers)concurrent.futuresr   rw   get_running_loopsubmitr   RuntimeError)	r:   r;   r   rk   r   r   _executorfutures	   ````     r!   logging_hook)_OPTIONAL_PresidioPIIMasking.logging_hook   sm     	:	- 	-	%((*A $2h!9}} 322  	%"$$	%s.   A, !A	A, 
A)%A, )A, ,B ?B c           	        #    US:X  d  US:X  a  UR                  SS5      n/ nUc  X4$ U R                  U5      nU HL  nSnUS   c  M  [        US   [        5      (       d  M'  US   nUR	                  U R                  USUS95        MN     [        R                  " U6 I Sh  vN n	[        U	5       H(  u  p[        XJ   S   [        5      (       d  M!  UXJ   S'   M*     [        R                  " S	U 35        XAS'   X4$  N[7f)
z;
Masks the input before logging to langfuse, datadog, etc.
rn   acompletionro   N rp   Frq   rr   )rs   rt   ru   r   rv   rf   rw   rx   ry   r
   rz   )r:   r;   r   rk   ro   r{   rI   r|   text_strr}   r~   r.   s               r!   r   /_OPTIONAL_PresidioPIIMasking.async_logging_hook  s"     %m)C'-zz*d'CHE~%"JJ6ROY<'a	lC00 |HLL!)-2,; '   &nne44I%i0hoi8#>>  O! 1
 !%%>xjI "*:~ 5s   AD #?D "C>#,D ,D r_   c                    #    [         R                  " SU R                   S[        U5       35        U R                  SL a  [        R                  SL a  U$ [        U[        5      (       Ga  [        UR                  S   [        5      (       d  [        UR                  S   R                  R                  [        5      (       a  [         R                  " SU R                   SUR                  S   R                  R                   35        U R                  R                  5        HS  u  pEUR                  S   R                  R                  R                  XE5      UR                  S   R                  l
        MU     U$ 7f)zU
Output parse the response object to replace the masked tokens with user sent values
z(PII Masking Args: self.output_parse_pii=z; type of response=Fr   zself.pii_tokens: z; initial response: )r
   rW   r)   typerD   ru   r   choicesr   messagerp   r   r1   rP   rT   )r:   rj   rh   r_   keyvalues         r!   async_post_call_success_hook9_OPTIONAL_PresidioPIIMasking.async_post_call_success_hookI  sB     	""6t7L7L6MM`aefnao`pq	
   E)g.F.F%.OOh..zQ!18
 8
 (**1-55==sCC$**''88LXM]M]^_M`MhMhMpMpLqr #'//"7"7"9JC:B:J:J;gggggc&9 $$Q'//7 #: s   E<E>c                 d    SU;   a*  US   nUR                  S5      nU(       a  [        S0 UD6nU$ g )Nmetadataguardrail_configr   )rs   r   )r:   rj   	_metadata_guardrail_config_presidio_configs        r!   rt   D_OPTIONAL_PresidioPIIMasking.get_presidio_settings_from_request_dataf  sA     Z(I ).@ A #;#P>O#P ''r    c                      [         R                  " U5        [        R                  (       a  [	        U5        g g ! [
         a     g f = f)N)r
   rW   rD   set_verboseprintr7   )r:   print_statements     r!   print_verbose*_OPTIONAL_PresidioPIIMasking.print_verboser  s=    	 &&7""o& # 		s   6: 
AA)r5   r+   r&   r)   r1   r'   r(   )FNNNFNN)NN)r   r   r   r   user_api_key_cacher5   boolr   dictr   r0   r9   r   rf   r   r   r   r   r   r   r   r   r   r   r   r   rt   r   r   __classcell__)r>   s   @r!   r#   r#   *   s   
 #-1486:+059'+)
)
 %TN)
 %-SM	)

 '/sm)
 #4.)
 &.c])
 tn)
 )
Z 596:&$,SM& '/sm&PGG G "":;	G
 
GR1)1 1 	1
 1f%%$'%47%	tSy	%@))$')47)	tSy	)V * '8-GH	:

	*	+
 r    r#   )rw   r3   rY   typingr   r   r   r   r   rU   pydanticr   rD   r	   litellm._loggingr
   litellm.caching.cachingr   %litellm.integrations.custom_guardrailr   litellm.proxy._typesr   litellm.types.guardrailsr   litellm.utilsr   r   r   r   r   r#   r   r    r!   <module>r      sU       4 4     1 - A / 8 #y #N? Nr    