Ë
    tPêjÖ¡  ã                   óx  — d dl Z d dlZd dlZd dlmZmZmZmZmZ 	 d dl	Z	dZ
	 d dlZdZd dlmZ d dlZd dlmZ d dlmZ d dlmZmZ d d	lmZ d d
lmZmZ d dlmZ d dlmZm Z  d dl!m"Z"  G d„ de«      Z#erd dl$m%Z%  G d„ de&«      Z' G d„ de&«      Z( G d„ de«      Z)y# e$ r dZ	dZ
Y Œ‡w xY w# e$ r dZdZY Œ�w xY w)é    N)ÚTYPE_CHECKINGÚAnyÚFinalÚLiteralÚ	TypedDictTF)ÚHTTPException)Ú	DualCache)Úverbose_proxy_logger)ÚCustomGuardrailÚlog_guardrail_information)Údc)Úget_async_httpx_clientÚhttpxSpecialProvider)ÚUserAPIKeyAuth)Úbuild_inspection_messagesÚhas_non_string_content)ÚGuardrailEventHooksc                   ót   — e Zd ZU dZeed<   eeef   ed<   eeeeee	f      f   ed<   eeeef      dz  ed<   y)ÚLassoResponsez'Type definition for Lasso API response.Úviolations_detectedÚdeputiesÚfindingsNÚmessages)
Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚboolÚ__annotations__ÚdictÚstrÚlistÚobject© ó    úy/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.pyr   r   3   sO   … Ù1àÓØ�3˜�9‰oÓØ�3˜˜T # v +Ñ.Ñ/Ð/Ñ0Ó0Ø�4˜˜S˜‘>Ñ" TÑ)Ô)r%   r   )ÚGuardrailConfigModelc                   ó   — e Zd ZdZy)ÚLassoGuardrailMissingSecretsz/Exception raised when Lasso API key is missing.N©r   r   r   r   r$   r%   r&   r)   r)   @   s   „ Ú9r%   r)   c                   ó   — e Zd ZdZy)ÚLassoGuardrailAPIErrorz=Exception raised when there's an error calling the Lasso API.Nr*   r$   r%   r&   r,   r,   D   s   „ ÚGr%   r,   c                   ó&  ‡ — e Zd ZdZedee   fd„«       Z	 	 	 	 	 	 d<dedz  dedz  dedz  dedz  d	edz  d
e	dz  fˆ fd„Z
ed=dedededefd„«       Zededeedz  edz  eeef   dz  f   fd„«       Zdefd„Zededededed   deez  ez  dz  f
d„«       Zedededed   defd„«       Zededefd„«       Zdededefd„Z	 d>dededed   fd„Zdededed   d eeeef      def
d!„Zdededed   d eeeef      d"edefd#„Zd$eeeef      d%eeeef      deeeef      fd&„Z d'ee   d(eeeeef   f   dee   fd)„Z!d*eded   ddfd+„Z"ded   d,eeef   ddfd-„Z#d eeeef      deeeef      fd.„Z$dededeeef   fd/„Z%	 d>d eeeef      dededed   deeef   f
d0„Z&	 d=d1eeef   d2eeef   d3edz  de'fd4„Z(d,e'ddfd5„Z)d,e'dee   fd6„Z*d,e'dee   fd7„Z+d8e,jZ                  d%eeeef      ddfd9„Z.ede/d:   dz  fd;„«       Z0ˆ xZ1S )?ÚLassoGuardrailz§
    Lasso Security Guardrail integration for LiteLLM.

    Provides content moderation, PII detection, and policy enforcement
    through the Lasso Security API.
    Úreturnc                 ó`   — t         j                  t         j                  t         j                  gS ©N)r   Úpre_callÚduring_callÚ	post_call)Úclss    r&   Úget_supported_event_hooksz(LassoGuardrail.get_supported_event_hooksP   s)   € ô  ×(Ñ(Ü×+Ñ+Ü×)Ñ)ð
ð 	
r%   NÚlasso_api_keyÚapi_keyÚapi_baseÚuser_idÚconversation_idÚmaskc                 ó¸  •— |j                  dt        | j                  «       «      «       t        t        j
                  ¬«      | _        |xs# |xs t        j                  j                  d«      | _
        |xs t        j                  j                  d«      | _        |xs t        j                  j                  d«      | _        |xs d| _        | j                  €t        d«      ‚|xs t        j                  d«      xs d	| _        t#        j$                  d
|j                  dd«      |j                  dd«      | j                  «       t'        ‰| �P  di |¤Ž y )NÚsupported_event_hooks)Úllm_providerÚLASSO_API_KEYÚLASSO_USER_IDÚLASSO_CONVERSATION_IDFú‹Couldn't get Lasso api key, either set the `LASSO_API_KEY` in the environment or pass it as a parameter to the guardrail in the config fileÚLASSO_API_BASEz(https://server.lasso.security/gateway/v3z9Lasso guardrail initialized: %s, event_hook: %s, mask: %sÚguardrail_nameÚunknownÚ
event_hookr$   )Ú
setdefaultr"   r6   r   r   ÚGuardrailCallbackÚasync_handlerÚosÚenvironÚgetr7   r:   r;   r<   r)   Úgetenvr9   r
   ÚdebugÚsuperÚ__init__)	Úselfr7   r8   r9   r:   r;   r<   ÚkwargsÚ	__class__s	           €r&   rQ   zLassoGuardrail.__init__X   s  ø€ ð 	×ÑÐ1´4¸×8VÑ8VÓ8XÓ3YÔZÜ3ÔAU×AgÑAgÔhˆÔØ*ÒX¨gÒX¼¿¹¿¹ÈÓ9XˆÔØÒA¤"§*¡*§.¡.°Ó"AˆŒØ.ÒY´"·*±*·.±.ÐAXÓ2YˆÔØ’M˜EˆŒ	à×ÑÐ%Ü.ðMóð ð
 !Òm¤B§I¡IÐ.>Ó$?ÒmÐCmˆŒä×"Ñ"ØGØ�J‰JÐ'¨Ó3Ø�J‰J�| YÓ/Ø�I‰Iô		
ô 	‰ÑÑ"˜6Ó"r%   ÚobjÚfieldÚdefaultc                 ó`   — t        | t        «      r| j                  ||«      S t        | ||«      S )z4Get a field from either a dict or a Pydantic object.)Ú
isinstancer    rM   Úgetattr)rU   rV   rW   s      r&   Ú
_get_fieldzLassoGuardrail._get_fieldz   s.   € ô �cœ4Ô Ø—7‘7˜5 'Ó*Ð*Ü�s˜E 7Ó+Ð+r%   Úcallc                 ó&  — t         j                  } || d«      } || d«      }|s|ddfS  ||d«      } ||d«      }d}|r-	 t        j                  |«      }t        |t        «      r|}nd|i}|||fS # t        j                  t
        f$ r d}Y Œ9w xY w)zËExtract (call_id, name, parsed_input) from a tool call.

        Handles both dict-style and Pydantic object-style tool_calls.
        Parses the JSON arguments string into a dict when possible.
        ÚidÚfunctionNÚnameÚ	arguments)r.   r[   ÚjsonÚloadsÚJSONDecodeErrorÚ	TypeErrorrY   r    )r\   rM   Úcall_idÚfuncr`   Úargs_strÚ
input_dataÚparseds           r&   Ú_extract_tool_call_fieldsz(LassoGuardrail._extract_tool_call_fields�   s´   € ô $×.Ñ.ˆÙ˜T 4›ˆÙ˜$ 
Ó+ˆÙØ˜D $Ð&Ð&Ù˜$ Ó'ˆÙ˜d KÓ0ˆØ/3ˆ
ÙðÜŸ™ HÓ-�ô ˜&¤$Ô'Ø#‘
ð
 *¨8Ð4�
Ø˜˜jÐ(Ð(øô ×(Ñ(¬)Ð4ò Ø’ðús   ÁA2 Á2BÂBc                 ó¸   — t         r#t        �t        t        j                  «       «      S t	        j
                  d«       t        t        j                  «       «      S )zš
        Generate a ULID (Universally Unique Lexicographically Sortable Identifier).
        Falls back to UUID if ULID library is not available.
        z&ULID library not available, using UUID)ÚULID_AVAILABLEÚulidr!   ÚULIDr
   rO   ÚuuidÚuuid4)rR   s    r&   Ú_generate_ulidzLassoGuardrail._generate_ulid    s=   € õ
 œdÐ.Ü”t—y‘y“{Ó#Ð#ä ×&Ñ&Ð'OÔPÜ”t—z‘z“|Ó$Ð$r%   Úuser_api_key_dictÚcacheÚdataÚ	call_type)
Ú
completionÚtext_completionÚ
embeddingsÚimage_generationÚ
moderationÚaudio_transcriptionÚpass_through_endpointÚrerankÚmcp_callÚanthropic_messagesc              ƒ   óÈ   K  — t         j                  }| j                  ||¬«      dur|S | j                  |t        «       | j                  |t        d¬«      ƒ d{  –—† S 7 Œ­w)z™
        Runs before the LLM API call to validate and potentially modify input.
        Uses 'PROMPT' messageType as this is input to the model.
        ©ru   Ú
event_typeTÚPROMPT©Úmessage_typeN)r   r2   Úshould_run_guardrailÚ _get_or_generate_conversation_idÚglobal_cacheÚ_run_lasso_guardrail)rR   rs   rt   ru   rv   rƒ   s         r&   Úasync_pre_call_hookz"LassoGuardrail.async_pre_call_hook«   sb   è ø€ ô0 2E×1MÑ1Mˆ
Ø×$Ñ$¨$¸:Ð$ÓFÈdÑRØˆKð 	×-Ñ-¨d´LÔAà×.Ñ.¨t´\ÐPXÐ.ÓY×YÐYÐYús   ‚AA"ÁA ÁA")rw   ry   rz   r{   r|   Ú	responsesr   r€   c              ƒ   ó”   K  — t         j                  }| j                  ||¬«      dur|S | j                  ||d¬«      ƒ d{  –—† S 7 Œ­w)z�
        This is used for during_call moderation.
        Uses 'PROMPT' messageType as this runs concurrently with input processing.
        r‚   Tr„   r…   N)r   r3   r‡   rŠ   )rR   ru   rs   rv   rt   rƒ   s         r&   Úasync_moderation_hookz$LassoGuardrail.async_moderation_hookÍ   sO   è ø€ ô, 2E×1PÑ1Pˆ
Ø×$Ñ$¨$¸:Ð$ÓFÈdÑRØˆKà×.Ñ.¨t°UÈÐ.ÓR×RÐRÐRús   ‚?AÁAÁAc           
   ƒ   óö  K  — t         j                  }| j                  ||¬«      dur|S t        |t        j
                  «      �rÈg }|j                  D ]Ž  }t        |d«      sŒ|j                  }|j                  r|j                  d|j                  dœ«       t        |dd«      xs g D ]5  }| j                  |«      \  }	}
}|	r|
sŒ|j                  dd	|	|
|d
œdœ«       Œ7 Œ� |�r||j                  d«      dœ}| j                  r¸| j                  |t         «      }| j#                  ||t         d«      }| j$                  › d�}	 | j'                  |||¬«      ƒ d{  –—† }| j)                  |«       |j                  d«      }|j                  d«      r)|r'| j+                  ||«       t-        j.                  d«       |S | j9                  |t         d¬«      ƒ d{  –—†  t-        j.                  d«       |S t-        j:                  d«       |S t-        j:                  dt=        |«      «       |S 7 ŒÑ# t0        $ r;}t        |t2        «      r|‚t-        j4                  d|«       t7        d|› �«      ‚d}~ww xY w7 Œœ­w)z�
        Runs after the LLM API call to validate the response.
        Uses 'COMPLETION' messageType as this is output from the model.
        r‚   TÚmessageÚ	assistant©ÚroleÚcontentÚ
tool_callsNÚmodelÚtool_use©Útyper^   r`   ÚinputÚlitellm_call_id)r   r›   Ú
COMPLETIONú
/classifix©ÚheadersÚpayloadÚapi_urlr   r   z'Applied Lasso masking to model responsez$Error in post-call Lasso masking: %sz#Failed to apply post-call masking: )rt   r†   z$Post-call Lasso validation completedz&No response messages found to validatez/Unexpected response type for post-call hook: %s)r   r4   r‡   rY   ÚlitellmÚModelResponseÚchoicesÚhasattrr�   r”   ÚappendrZ   rk   rM   r<   Ú_prepare_headersr‰   Ú_prepare_payloadr9   Ú_call_lasso_apiÚ_process_lasso_responseÚ _apply_masking_to_model_responser
   rO   Ú	Exceptionr   Úerrorr,   rŠ   Úwarningr™   )rR   ru   rs   Úresponserƒ   Úresponse_messagesÚchoiceÚmsgr\   rf   r`   ri   Úresponse_datarŸ   r    r¡   Úlasso_responseÚmasked_messagesÚes                      r&   Úasync_post_call_success_hookz+LassoGuardrail.async_post_call_success_hooké   s’  è ø€ ô 2E×1NÑ1Nˆ
Ø×$Ñ$¨$¸:Ð$ÓFÈdÑRØˆOô �h¤× 5Ñ 5Õ6Ø@BÐØ"×*Ñ*ò �Ü˜v yÔ1ØØ—n‘n�Ø—;’;Ø%×,Ñ,°kÈcÏkÉkÑ-ZÔ[Ü# C¨°tÓ<ÒBÀò �DØ04×0NÑ0NÈtÓ0TÑ-�G˜T :Ù"©$Ø Ø%×,Ñ,à$+à(2Ø&-Ø(,Ø)3ñ	(ñõ
ñ	ðò, !ð !2Ø'+§x¡xÐ0AÓ'Bñ(�ð —9’9Ø%)×%:Ñ%:¸=Ì,Ó%W�GØ%)×%:Ñ%:Ð;LÈmÔ]iÐkwÓ%x�GØ(,¯© °jÐ%A�Gð`Ø/3×/CÑ/CÈGÐ]dÐnuÐ/CÓ/v×)v˜Ø×4Ñ4°^ÔDð 2@×1CÑ1CÀJÓ1O˜Ø)×-Ñ-Ð.CÔDÉØ ×AÑAÀ(ÈOÔ\Ü0×6Ñ6Ð7`Ôað ˆð ×3Ñ3°MÌÐdpÐ3Óq×qÐqÜ(×.Ñ.Ð/UÔVð ˆô	 %×,Ñ,Ð-UÔVð ˆô !×(Ñ(Ð)ZÔ\`ÐaiÓ\jÔkàˆð- *wùô %ò `Ü% a¬Ô7Ø"#˜GÜ,×2Ñ2Ð3YÐ[\Ô]Ü4Ð7ZÐ[\ÐZ]Ð5^Ó_Ð_ûð	`úð rúsJ   ‚EI9ÅH0 ÅH.ÅA H0 Æ=I9ÇI7ÇAI9È.H0 È0	I4È96I/É/I4É4I9c                 ó°  — | j                   r| j                   S |j                  d«      }|s| j                  «       S d|› �}	 |j                  |«      }|r|S 	 | j                  «       }	 |j                  ||d¬«       |S # t        $ r }t        j                  d|«       Y d}~ŒKd}~ww xY w# t        $ r!}t        j                  d|«       Y d}~|S d}~ww xY w)aD  
        Get or generate a conversation_id for this request.

        This method ensures session consistency by using litellm_call_id as a cache key.
        The same conversation_id is used for both pre-call and post-call hooks within
        the same request, enabling proper conversation grouping in Lasso UI.

        Example:
            >>> guardrail = LassoGuardrail(lasso_api_key="key")
            >>> data = {"litellm_call_id": "call_123"}
            >>> conversation_id = guardrail._get_or_generate_conversation_id(data, cache)
            >>> # Returns consistent ID for same litellm_call_id

        Args:
            data: The request data containing litellm_call_id
            cache: The cache instance for storing conversation_id

        Returns:
            str: The conversation_id to use for this request
        r›   zlasso_conversation_id:zCache retrieval failed: %sNi  )ÚttlzCache storage failed: %s)r;   rM   rr   Ú	get_cacher¬   r
   r®   Ú	set_cache)rR   ru   rt   r›   Ú	cache_keyÚcached_conversation_idr¶   Úgenerated_ids           r&   rˆ   z/LassoGuardrail._get_or_generate_conversation_id8  só   € ð, ×ÒØ×'Ñ'Ð'ð "&§¡Ð*;Ó!<ˆáà×&Ñ&Ó(Ð(ð 4°OÐ3DÐEˆ	ð	JØ,1¯O©O¸IÓ,FÐ"Ù%Ø-Ð-ð &ð #×1Ñ1Ó3ˆð	HØ�O‰O˜I |¸ˆOÔ>ð Ðøô ò 	JÜ ×(Ñ(Ð)EÀq×IÑIûð	Jûô ò 	HÜ ×(Ñ(Ð)CÀQ×GÐGàÐûð	Hús0   ÁA? Á)B+ Á?	B(ÂB#Â#B(Â+	CÂ4CÃCr†   )r„   rœ   c              ƒ   ó„  K  — |j                  d«      xs g }|r| j                  |«      ng }t        |«      }|j                  d«      �|j                  t	        d|d   i«      «       |s|S | j
                  r(t        |«      s| j                  |||||«      ƒ d{  –—† S | j                  ||||«      ƒ d{  –—† S 7 Œ 7 Œ­w)aå  
        Run the Lasso guardrail with the specified message type.

        This is the core method that handles both classification and masking workflows.
        It chooses the appropriate API endpoint based on the masking configuration
        and processes the response according to Lasso's action-based system.

        Workflow:
        1. Validate messages are present
        2. Prepare headers and payload
        3. Choose API endpoint (classify vs classifix)
        4. Call Lasso API
        5. Process response and apply masking if needed
        6. Handle blocking vs non-blocking violations

        Args:
            data: The request data containing messages
            cache: The cache instance for storing conversation_id (optional for post-call)
            message_type: Either "PROMPT" for input or "COMPLETION" for output

        Raises:
            LassoGuardrailAPIError: If the Lasso API call fails
            HTTPException: If blocking violations are detected
        r   rš   N)	rM   Ú#_expand_messages_for_classificationÚlenÚextendr   r<   r   Ú_handle_maskingÚ_handle_classification)rR   ru   rt   r†   Úraw_messagesr   Úmessages_counts          r&   rŠ   z#LassoGuardrail._run_lasso_guardrailm  sÀ   è ø€ ð< 8<·x±xÀ
Ó7KÒ7QÈrˆÙco¨×)QÑ)QÐR^Ô)_ÐuwˆÜ # H£ˆØ�8‰8�GÓÐ(ð �O‰OÔ5°wÀÀWÁÐ6NÓOÔPÙØˆKð �9Š9Ô3°DÔ9Ø×-Ñ-¨d°E¸<ÈÐSaÓb×bÐbØ×0Ñ0°°u¸lÈHÓU×UÐUð cøØUús$   ‚BC ÂB<ÂC Â7B>Â8C Â>C r   c              ƒ   ó  K  — 	 | j                  ||«      }| j                  ||||«      }| j                  ||¬«      ƒ d{  –—† }| j                  |«       |S 7 Œ# t        $ r'}| j                  ||«      ƒ d{  –—†7   |cY d}~S d}~ww xY w­w)z&Handle classification without masking.)rŸ   r    N)r§   r¨   r©   rª   r¬   Ú_handle_api_error)	rR   ru   rt   r†   r   rŸ   r    r¯   r¶   s	            r&   rÄ   z%LassoGuardrail._handle_classificationŸ  s‘   è ø€ ð	Ø!×2Ñ2°4¸Ó?ˆGØ!×2Ñ2°8¸TÀ5È,ÓWˆGØ$(×$8Ñ$8ÀÐRYÐ$8Ó$Z×ZˆHØ×(Ñ(¨Ô2ØˆKð [ùô ò 	Ø×(Ñ(¨¨LÓ9×9Ñ9Ø�Kûð	üsR   ‚B„<A Á AÁA ÁBÁA Á	B
Á#BÁ8A;Á9BÁ?B
Â BÂB
Â
BrÆ   c              ƒ   óú  K  — 	 | j                  ||«      }| j                  ||||«      }| j                  › d�}| j                  |||¬«      ƒ d{  –—† }	| j	                  |	«       |	j                  d«      }
|	j                  d«      r·|
rµ|
d| }|
|d }|j                  d«      r| j                  |d   |«      |d<   t        |j                  d«      t        «      rH|D �cg c]'  }t        |j                  d«      t        «      sŒ#|d   ‘Œ) }}|rdj                  |«      |d<   | j                  |t        |	«      «       |S 7 Œðc c}w # t        $ r'}| j                  ||«      ƒ d{  –—†7   |cY d}~S d}~ww xY w­w)	a  Handle masking with classifix endpoint.

        ``messages_count`` is the number of inspected items derived from
        ``data["messages"]``; any items beyond that index came from
        ``data["input"]`` and must be written back there, not into messages.
        r�   rž   Nr   r   rš   r”   ú
)r§   r¨   r9   r©   rª   rM   Ú_map_masked_messages_backrY   r!   ÚjoinÚ_log_masking_appliedr    r¬   rÈ   )rR   ru   rt   r†   r   rÆ   rŸ   r    r¡   r¯   ÚmaskedÚmasked_for_messagesÚmasked_for_inputr²   Ú
text_partsr¶   s                   r&   rÃ   zLassoGuardrail._handle_masking±  sy  è ø€ ð	Ø!×2Ñ2°4¸Ó?ˆGØ!×2Ñ2°8¸TÀ5È,ÓWˆGØ $§¡˜¨jÐ9ˆGØ$(×$8Ñ$8ÀÐRYÐcjÐ$8Ó$k×kˆHØ×(Ñ(¨Ô2ð
 %ŸL™L¨Ó4ˆFØ�|‰|Ð1Ô2±vØ-3°O°^Ð-DÐ#Ø*0°°Ð*AÐ Ø—8‘8˜JÔ'Ø'+×'EÑ'EÀdÈ:ÑFVÐXkÓ'l�D˜Ñ$ô ˜dŸh™h wÓ/´Ô5Ø<LÖ!t°SÔPZÐ[^×[bÑ[bÐclÓ[mÔorÕPs # i£.Ð!t�JÐ!tÙ!Ø(,¯	©	°*Ó(=˜˜W™Ø×)Ñ)¨,¼¸X»ÔGàˆKð) lúò "uøô ò 	Ø×(Ñ(¨¨LÓ9×9Ñ9Ø�Kûð	üsl   ‚E;„AE ÁEÁBE Ã $EÄEÄ4E Å E;ÅE ÅE Å	E8ÅE3Å&E)Å'E3Å-E8Å.E;Å3E8Å8E;Úoriginal_messagesrµ   c                 óÂ  — i }i }g }|D ]²  }|j                  d«      }t        |t        «      rm|j                  d«      dk(  r|j                  d«      }|sŒL|||<   ŒR|j                  d«      dk(  sŒg|j                  d«      }	|	sŒ{|j                  dd«      ||	<   Œ‘t        |t        «      sŒ¢|j	                  |«       Œ´ t        d„ |D «       «      }
|
t        |«      k(  }|s%|r#t        j                  d	|
t        |«      d
œ¬«       g }d}|D �]P  }t        |«      }|j                  d«      }|j                  d«      }|dk(  r |j                  d«      }|r÷||v ró||   |d<   nêt        |t        «      rN|rL|r|t        |«      k  r||   |d<   |dz  }|dk(  r¶|j                  d«      r¥| j                  |d   |«      |d<   nŒt        |t        «      rL|r|t        |«      k  r||   |d<   |dz  }|dk(  rZ|j                  d«      rI| j                  |d   |«      |d<   n0|dk(  r+|s)|j                  d«      r| j                  |d   |«      |d<   |j	                  |«       �ŒS |S )aï  Map Lasso-format masked messages back onto the original OpenAI-format messages.

        Lasso receives expanded messages (tool_use / tool_result blocks) and returns them
        in the same Lasso-internal format with sensitive values replaced.  Writing those
        blocks straight into data["messages"] would corrupt the OpenAI-compatible schema
        the downstream provider expects.  This helper re-applies only the masked content
        while preserving the original structure.
        r”   r™   r—   r^   Útool_resultÚtool_use_idÚ c              3   óæ   K  — | ]i  }|j                  d «      dk7  rSt        |j                  d«      t        «      r|j                  d«      st        |j                  d«      t        «      rd–— Œk y­w)r“   Útoolr”   é   N)rM   rY   r!   r"   )Ú.0Úms     r&   ú	<genexpr>z;LassoGuardrail._map_masked_messages_back.<locals>.<genexpr>ÿ  s]   è ø€ ò )
àØ�u‰u�V‹} Ò&Ü˜QŸU™U 9Ó-¬sÔ3¸¿¹¸iÔ8HÌZÐXY×X]ÑX]Ð^gÓXhÔjnÔMoô ñ)
ùs   ‚A/A1z5Lasso masked-text count mismatch; skipping text remap©Úoriginal_text_countÚmasked_text_count©Úextrar   r“   rØ   Útool_call_idrÙ   r‘   r•   )rM   rY   r    r!   r¦   ÚsumrÁ   r
   r®   Ú_update_tool_calls_from_maskedr"   )rR   rÒ   rµ   Úmasked_tool_useÚmasked_tool_resultÚmasked_textr²   r”   rf   rÕ   rÞ   Úapply_text_cursorÚresultÚtext_cursorÚorig_msgr“   râ   s                    r&   rË   z(LassoGuardrail._map_masked_messages_backÜ  sˆ  € ð @BˆØ46ÐØ(*ˆà"ò 	,ˆCØ—g‘g˜iÓ(ˆGÜ˜'¤4Ô(Ø—;‘;˜vÓ&¨*Ò4Ø%Ÿk™k¨$Ó/�GÚØ3:˜¨Ò0Ø—[‘[ Ó(¨MÓ9Ø")§+¡+¨mÓ"<�KÚ"Ø:A¿+¹+ÀiÐQSÓ:TÐ*¨;Ò7Ü˜G¤SÕ)Ø×"Ñ" 7Õ+ð	,ô" &)ñ )
à&ô)
ó &
Ðð $7¼#¸kÓ:JÑ#JÐÙ ¡[Ü ×(Ñ(ØGà+>Ü),¨[Ó)9ñõð 24ˆØˆà)ó 	ˆHÜ�x“.ˆCØ—7‘7˜6“?ˆDØ—g‘g˜iÓ(ˆGà�vŠ~Ø"Ÿw™w ~Ó6�Ù LÐ4FÑ$FØ%7¸Ñ%E�C˜	’Nä˜G¤SÔ)©gÙ$¨´s¸;Ó7GÒ)GØ%0°Ñ%=�C˜	‘NØ 1Ñ$�KØ˜;Ò&¨8¯<©<¸Ô+EØ(,×(KÑ(KÈHÐUaÑLbÐdsÓ(t�C˜Ò%ä˜G¤TÔ*ñ %¨´s¸;Ó7GÒ)GØ%0°Ñ%=�C˜	‘NØ 1Ñ$�KØ˜;Ò&¨8¯<©<¸Ô+EØ(,×(KÑ(KÈHÐUaÑLbÐdsÓ(t�C˜Ò%à˜Ò$©W¸¿¹ÀlÔ9SØ$(×$GÑ$GÈÐQ]ÑH^Ð`oÓ$p��LÑ!à�M‰M˜#Öð=	ð@ ˆr%   r•   rå   c                 ó�  — g }|D ]¾  }| j                  |d«      }|r—||v r“||   j                  d«      }|�}t        |t        «      rDt        |«      }t        |j                  di «      «      }t	        j
                  |«      |d<   ||d<   n)t        |dd«      }|rt	        j
                  |«      |_        |j                  |«       ŒÀ |S )zAReplace tool_call arguments with masked values returned by Lasso.r^   rš   Nr_   ra   )	r[   rM   rY   r    rb   ÚdumpsrZ   ra   r¦   )	rR   r•   rå   Úupdatedr\   rf   Úmasked_inputÚ	func_dictÚfunc_objs	            r&   rä   z-LassoGuardrail._update_tool_calls_from_masked4  sÇ   € ð ˆØò 	!ˆDØ—o‘o d¨DÓ1ˆGÙ˜7 oÑ5Ø.¨wÑ7×;Ñ;¸GÓD�ØÐ+Ü! $¬Ô-Ü# D›z˜Ü$(¨¯©°*¸bÓ)AÓ$B˜	Ü15·±¸LÓ1I˜	 +Ñ.Ø+4˜˜ZÒ(ä#*¨4°¸TÓ#B˜Ù#Ü15·±¸LÓ1I˜HÔ.Ø�N‰N˜4Õ ð	!ð ˆr%   r­   c           	   ƒ   ó  K  — t        |t        «      r|‚t        j                  d|t	        | dd«      |t        |«      j                  dœ¬«       t        r©t        |t        j                  «      rt        d«      ‚t        |t        j                  «      rj|j                  j                  dk(  rt        d«      ‚|j                  j                  d	k(  rt        d
«      ‚t        d|j                  j                  › �«      ‚t        d|› �«      ‚­w)z,Handle API errors with specific error types.zError calling Lasso API: %srE   rF   )rE   r†   Ú
error_typerà   zLasso API timeouti‘  zInvalid API keyi­  zLasso API rate limit exceededzAPI error: z0Failed to verify request safety with Lasso API: )rY   r   r
   r­   rZ   r™   r   ÚHTTPX_AVAILABLEÚhttpxÚTimeoutExceptionr,   ÚHTTPStatusErrorr¯   Ústatus_coder)   )rR   r­   r†   s      r&   rÈ   z LassoGuardrail._handle_api_errorL  sð   è ø€ ô �eœ]Ô+ØˆKô 	×"Ñ"Ø)Øä")¨$Ð0@À)Ó"LØ ,Ü" 5›k×2Ñ2ñõ	
õ Ü˜%¤×!7Ñ!7Ô8Ü,Ð-@ÓAÐAÜ˜E¤5×#8Ñ#8Ô9Ø—>‘>×-Ñ-°Ò4Ü6Ð7HÓIÐIØ—^‘^×/Ñ/°3Ò6Ü0Ð1PÓQÐQä0°;¸u¿~¹~×?YÑ?YÐ>ZÐ1[Ó\Ð\ô %Ð'WÐX]ÐW^Ð%_Ó`Ð`ùs   ‚D
Dr¯   c                 óÒ   — t        | dd«      }t        j                  dt        | dd«      |t        |j	                  di «      «      t        |j	                  dg «      «      |dœ¬«       y	)
z0Log masking application with structured context.r;   rF   zLasso masking appliedrE   r   r   )rE   r†   Úviolations_countÚmasked_fieldsr;   rà   N)rZ   r
   rO   rÁ   rM   )rR   r†   r¯   r;   s       r&   rÍ   z#LassoGuardrail._log_masking_appliedo  sb   € ô ")¨Ð/@À)Ó!LˆÜ×"Ñ"Ø#ä")¨$Ð0@À)Ó"LØ ,Ü$'¨¯©°ZÀÓ(DÓ$EÜ!$ X§\¡\°*¸bÓ%AÓ!BØ#2ñö		
r%   c           
      óä  — g }|D �]Ý  }|j                  dd«      }|j                  d«      }|dk(  r±|j                  d«      }|st        j                  d«       ŒUt        |t        «      rY|D �cg c]<  }t        |t
        «      r*|j                  d«      dk(  r|j                  d«      r|d   ‘Œ> }}d	j                  |«      }	n|xs d}	|j                  d
d||	dœdœ«       ŒÝt        |t        «      rm|D �cg c]<  }t        |t
        «      r*|j                  d«      dk(  r|j                  d«      r|d   ‘Œ> }}|r:|j                  |d	j                  |«      dœ«       n|r|j                  ||dœ«       |dk(  s�Œw|j                  d«      xs g D ]O  }
| j                  |
«      \  }}}|r|st        j                  d||dœ¬«       Œ7|j                  dd|||dœdœ«       ŒQ �Œà |S c c}w c c}w )u/  
        Convert raw OpenAI-format messages to Lasso API format with content blocks.

        - assistant messages with `tool_calls` â†’ assistant message per tool_use block
        - role=tool messages â†’ developer role + tool_result block
        - plain text messages pass through unchanged
        r“   rÖ   r”   rØ   râ   z*Skipping tool message without tool_call_idr™   ÚtextrÊ   Ú	developerrÔ   )r™   rÕ   r”   r’   r‘   r•   zSkipping malformed tool_call)rf   r`   rà   r–   r—   r˜   )	rM   r
   r®   rY   r"   r    rÌ   r¦   rk   )rR   r   Úexpandedr²   r“   r”   râ   ÚpartrÑ   Útool_result_contentr\   rf   r`   ri   s                 r&   rÀ   z2LassoGuardrail._expand_messages_for_classification�  s  € ð 13ˆØó D	ˆCØ—7‘7˜6 2Ó&ˆDØ—g‘g˜iÓ(ˆGà�vŠ~Ø"Ÿw™w ~Ó6�Ù#Ü(×0Ñ0Ð1]Ô^Øô ˜g¤tÔ,ð %,ö"à Ü% d¬DÔ1°d·h±h¸vÓ6FÈ&Ò6PÐUY×U]ÑU]Ð^dÔUeð ˜V›ð"�Jð "ð
 +/¯)©)°JÓ*?Ñ'à*1ª-°RÐ'Ø—‘à +à$1Ø+7Ø':ñ$ñô	ð ä˜'¤4Ô(ð !(öàÜ! $¬Ô-°$·(±(¸6Ó2BÀfÒ2LÐQU×QYÑQYÐZ`ÔQað ˜“Lð�
ð ñ
 Ø—O‘O¨T¸d¿i¹iÈ
Ó>SÑ$TÕUÙð —‘¨¸'Ñ BÔCà�{Ô"ØŸG™G LÓ1Ò7°Rò �DØ04×0NÑ0NÈtÓ0TÑ-�G˜T :Ù"©$Ü,×4Ñ4Ø:Ø.5¸tÑ"Dõð !Ø—O‘Oà$+à(2Ø&-Ø(,Ø)3ñ	(ñõ
òðeD	ðL ˆùòu"ùò,s   Á.AG(Ã6AG-c                 ó¶   — | j                   st        d«      ‚| j                   ddœ}| j                  r| j                  |d<   | j                  ||«      }||d<   |S )z*Prepare headers for the Lasso API request.rC   zapplication/json)zlasso-api-keyzContent-Typezlasso-user-idzlasso-conversation-id)r7   r)   r:   rˆ   )rR   ru   rt   rŸ   r;   s        r&   r§   zLassoGuardrail._prepare_headersÒ  sn   € à×!Ò!Ü.ðMóð ð "×/Ñ/Ø.ñ*
ˆð �<Š<Ø'+§|¡|ˆG�OÑ$ð "&×!FÑ!FÀtÈUÓ!Sˆà+:ˆÐ'Ñ(àˆr%   c                 óz  — ||ddidœ}| j                   r| j                   |d<   | j                  ||«      }||d<   |j                  d«      xs g }|ri| j                  }g }	|D ]O  }
 ||
d«      }|sŒ ||d«      }|sŒd|i} ||d	«      }|r||d	<    ||d
«      }|r||d
<   |	j	                  |«       ŒQ |	r|	|d<   |S )a¸  
        Prepare the payload for the Lasso API request.

        Args:
            messages: List of message objects (may contain tool_use/tool_result content blocks)
            message_type: Type of message - "PROMPT" for input, "COMPLETION" for output
            data: Request data (used for conversation_id generation and tools extraction)
            cache: Cache instance for storing conversation_id (optional for post-call)
        r™   r¢   )r   ÚmessageTypeÚsourceÚuserIdÚ	sessionIdÚtoolsr_   r`   ÚdescriptionÚ
parameters)r:   rˆ   rM   r[   r¦   )rR   r   ru   rt   r†   r    r;   Ú
tools_datarM   Útool_definitionsrØ   rg   r`   Útdr	  r
  s                   r&   r¨   zLassoGuardrail._prepare_payloadê  s  € ð" !Ø'ð ˜yÐ)ñ-
ˆð �<Š<Ø $§¡ˆG�HÑð "&×!FÑ!FÀtÈUÓ!SˆØ.ˆ�Ñð 6:·X±X¸gÓ5FÒ5LÈ"ˆ
ÙØŸ™ˆCØ&(ÐØ"ò ,�Ù˜4 Ó,�ÙØÙ˜4 Ó(�ÙØØ)/°¨�Ù! $¨Ó6�ÙØ(3�B�}Ñ%Ù   |Ó4�
ÙØ'1�B�|Ñ$Ø ×'Ñ'¨Õ+ð,ñ  Ø#3�˜Ñ àˆr%   rŸ   r    r¡   c              ƒ   ó  K  — |xs | j                   › d�}t        j                  d|j                  d«      «       | j                  j                  |||d¬«      ƒ d{  –—† }|j                  «        |j                  «       S 7 Œ$­w)z+Call the Lasso API and return the response.z	/classifyz&Calling Lasso API with messageType: %sr  g      $@)ÚurlrŸ   rb   ÚtimeoutN)r9   r
   rO   rM   rJ   ÚpostÚraise_for_statusrb   )rR   rŸ   r    r¡   r  r¯   s         r&   r©   zLassoGuardrail._call_lasso_api#  sƒ   è ø€ ð Ò; 4§=¡= /°Ð ;ˆÜ×"Ñ"Ð#KÈWÏ[É[ÐYfÓMgÔhØ $× 2Ñ 2× 7Ñ 7ØØØØð	 !8ó !
÷ 
ˆð 	×!Ñ!Ô#Ø�}‰}‹Ðð
ús   ‚ABÁBÁ%Bc                 ó  — |rˆ|j                  d«      du rt| j                  |«      }t        j                  d|«       | j	                  |«      }|r#t        ddddj                  |«      › �|dœ¬	«      ‚t        j                  d
|«       yyy)a  
        Process the Lasso API response and handle violations according to action types.

        This method implements the action-based blocking logic:
        - BLOCK: Raises HTTPException to stop request/response
        - AUTO_MASKING: Logs warning and continues (masking applied elsewhere)
        - WARN: Logs warning and continues

        Example Response:
            {
                "violations_detected": true,
                "findings": {
                    "jailbreak": [{
                        "action": "BLOCK",
                        "severity": "HIGH"
                    }]
                }
            }

        Args:
            response: The response dictionary from Lasso API

        Raises:
            HTTPException: If any finding has "action": "BLOCK"
        r   Tz'Lasso guardrail detected violations: %si�  zViolated Lasso guardrail policyzBlocking violations detected: z, )r­   Údetection_messager´   )rø   ÚdetailzCNon-blocking Lasso violations detected, continuing with warning: %sN)rM   Ú_parse_violated_deputiesr
   r®   Ú_check_for_blocking_actionsr   rÌ   Úinfo)rR   r¯   Úviolated_deputiesÚblocking_violationss       r&   rª   z&LassoGuardrail._process_lasso_response5  sŸ   € ñ4 ˜Ÿ™Ð%:Ó;¸tÑCØ'+×'DÑ'DÀXÓ'NÐÜ ×(Ñ(Ð)RÐTeÔfð *.×)IÑ)IÈ(Ó)SÐá"ä#Ø #à!BØ/MÈdÏiÉiÐXkÓNlÐMmÐ-nØ*2ñôð ô %×)Ñ)ØYÐ[lõð' Dˆ8r%   c                 ó  — g }|j                  di «      }|j                  «       D ]Y  \  }}t        |t        «      sŒ|D ]>  }t        |t        «      sŒ|j                  d«      dk(  sŒ)||vr|j                  |«        ŒY Œ[ |S )ad  
        Check findings for actions that should block the request/response.

        Examines the findings section of the Lasso response to identify which
        deputies have violations with "BLOCK" action. This enables granular
        control where some violations (like PII) can be masked while others
        (like jailbreaks) are blocked entirely.

        Args:
            response: The response dictionary from Lasso API

        Returns:
            List[str]: Names of deputies with blocking violations

        Example:
            >>> response = {
            ...     "findings": {
            ...         "jailbreak": [{"action": "BLOCK"}],
            ...         "pattern-detection": [{"action": "AUTO_MASKING"}]
            ...     }
            ... }
            >>> guardrail._check_for_blocking_actions(response)
            ['jailbreak']
        r   ÚactionÚBLOCK)rM   ÚitemsrY   r"   r    r¦   )rR   r¯   r  r   Údeputy_nameÚdeputy_findingsÚfindings          r&   r  z*LassoGuardrail._check_for_blocking_actionsf  sŠ   € ð2 &(ÐØ"Ÿ,™, z°2Ó6ˆà,4¯N©NÓ,<ò 	Ñ(ˆK˜Ü˜/¬4Õ0Ø.ò �GÜ! '¬4Õ0°W·[±[ÀÓ5JÈgÓ5UØ&Ð.AÑAØ/×6Ñ6°{ÔCÙñ	ð	ð #Ð"r%   c                 óp   — g }d|v r/|d   j                  «       D ]  \  }}|sŒ	|j                  |«       Œ |S )z0Parse the response to extract violated deputies.r   )r  r¦   )rR   r¯   r  ÚdeputyÚis_violateds        r&   r  z'LassoGuardrail._parse_violated_deputiesŒ  sK   € à#%ÐØ˜Ñ!Ø'/°
Ñ';×'AÑ'AÓ'Cò 5Ñ#�˜ÚØ%×,Ñ,¨VÕ4ð5ð !Ð r%   Úmodel_responsec                 ó  — i }g }|D ]s  }|j                  d«      }t        |t        «      r.|j                  d«      dk(  r|j                  d«      }|sŒL|||<   ŒRt        |t        «      sŒc|j	                  |«       Œu t        d„ |j                  D «       «      }|t        |«      k(  }	|	s%|r#t        j                  d|t        |«      dœ¬«       d	}
|j                  D �].  }t        |d
«      sŒ|j                  }|j                  r5|	r3|
t        |«      k  r%||
   |_        |
dz  }
t        j                  d|
«       t        |dd«      xs g D ]½  }| j                  |d«      }|sŒ||v sŒ||   j                  d«      }|€Œ4t        |t        «      r;|j                  di «      }t        |t        «      rBt!        j"                  |«      |d<   n)t        |dd«      }|rt!        j"                  |«      |_        t        j                  d|«       Œ¿ �Œ1 y)zZApply masking to the actual model response when mask=True and masked content is available.r”   r™   r—   r^   c              3   óh   K  — | ]*  }t        |d «      sŒ|j                  j                  sŒ'd–— Œ, y­w)r�   rÙ   N)r¥   r�   r”   )rÚ   Úcs     r&   rÜ   zBLassoGuardrail._apply_masking_to_model_response.<locals>.<genexpr>¨  s)   è ø€ Ò!s¨ÄwÈqÐR[ÕG\Ðab×ajÑaj×arÓar¤!Ñ!sùs   ‚2”2«2zGLasso masked-text count mismatch in model response; skipping text remaprÝ   rà   r   r�   rÙ   z(Applied masked text content to choice %sr•   Nrš   r_   ra   z1Applied masked tool_call arguments for call_id=%s)rM   rY   r    r!   r¦   rã   r¤   rÁ   r
   r®   r¥   r�   r”   rO   rZ   r[   rb   rí   ra   )rR   r%  rµ   rå   rç   Ú
masked_msgr”   rf   rÞ   Ú
apply_textrê   r±   r²   r\   rï   rg   s                   r&   r«   z/LassoGuardrail._apply_masking_to_model_response•  s÷  € ð @BˆØ(*ˆØ)ò 	,ˆJØ —n‘n YÓ/ˆGÜ˜'¤4Ô(¨W¯[©[¸Ó-@ÀJÒ-NØ!Ÿ+™+ dÓ+�ÚØ/6�O GÒ,Ü˜G¤SÕ)Ø×"Ñ" 7Õ+ð	,ô "Ñ!s¨^×-CÑ-CÔ!sÓsÐØ/´3°{Ó3CÑCˆ
Ù™kÜ ×(Ñ(ØYà+>Ü),¨[Ó)9ñõð ˆØ$×,Ñ,ó 	qˆFÜ˜6 9Ô-ØØ—.‘.ˆCà�{Š{™z¨k¼CÀÓ<LÒ.LØ)¨+Ñ6�”Ø˜qÑ �Ü$×*Ñ*Ð+UÐWbÔcä  \°4Ó8Ò>¸Bò q�ØŸ/™/¨$°Ó5�Ú˜w¨/Ò9Ø#2°7Ñ#;×#?Ñ#?ÀÓ#H�LØ#Ñ/Ü% d¬DÔ1Ø#'§8¡8¨J¸Ó#;˜DÜ)¨$´Ô5Ü48·J±J¸|Ó4L  [Ò 1ä#*¨4°¸TÓ#B˜DÙ#Ü15·±¸LÓ1I ¤Ü,×2Ñ2Ð3fÐhoÕpòqñ	qr%   r'   c                  ó   — ddl m}  | S )Nr   ©ÚLassoGuardrailConfigModel)Ú4litellm.types.proxy.guardrails.guardrail_hooks.lassor-  r,  s    r&   Úget_config_modelzLassoGuardrail.get_config_modelÍ  s   € õ	
ð )Ð(r%   )NNNNNFr1   )r„   )2r   r   r   r   Úclassmethodr"   r   r6   r!   r   rQ   Ústaticmethodr   r#   r[   Útupler    rk   rr   r   r   r	   r   r¬   r‹   rŽ   r·   rˆ   rŠ   rÄ   ÚintrÃ   rË   rä   rÈ   rÍ   rÀ   r§   r¨   r   r©   rª   r  r  r¢   r£   r«   r™   r/  Ú__classcell__)rT   s   @r&   r.   r.   H   sY  ø„ ñð ð
¨$Ð/BÑ*Cò 
ó ð
ð %)Ø"Ø#Ø"Ø&*Ø!ñ #à˜T‘zð #ð �t‘ð #ð ˜‘*ð	 #ð
 �t‘ð #ð ˜t™ð #ð �T‰kõ #ðD ñ,˜ð , Cð ,°&ð ,ÀCò ,ó ð,ð ð)Øð)à	ˆs�T‰z˜3 ™: t¨C°¨KÑ'8¸4Ñ'?Ð?Ñ	@ò)ó ð)ð<	% ó 	%ð ðZà)ðZð ðZð ð	Zð
 ð	"ñ
ðZð" 
�S‰˜4Ñ	 $Ñ	&ò#Zó ðZðB ðSàðSð *ðSð ð"ñ	
ð	Sð òSó ðSð6 ðLàðLð *òLó ðLð\3°Tð 3À)ð 3ÐPSó 3ðr 9Añ	0Vàð0Vð ð0Vð Ð4Ñ5ó	0Vðdàðð ðð Ð4Ñ5ð	ð
 �t˜C ˜KÑ(Ñ)ðð 
óð$)àð)ð ð)ð Ð4Ñ5ð	)ð
 �t˜C ˜KÑ(Ñ)ð)ð ð)ð 
ó)ðVVà  S¨# X¡Ñ/ðVð ˜d 3¨ 8™nÑ-ðVð 
ˆd�3˜�;ÑÑ	 ó	Vðpà˜‘Lðð ˜c 4¨¨V¨Ñ#4Ð4Ñ5ðð 
ˆf‰ó	ð0!aàð!að Ð4Ñ5ð!að 
ó	!aðF
àÐ4Ñ5ð
ð �s˜C�x‘.ð
ð 
ó	
ð$O¸DÀÀcÈ3ÀhÁÑ<Pð OÐUYÐZ^Ð_bÐdgÐ_gÑZhÑUió Oðb Tð °)ð ÀÀSÈ#ÀXÁó ð: 9Añ7à�t˜C ˜KÑ(Ñ)ð7ð ð7ð ð	7ð
 Ð4Ñ5ð7ð 
ˆc�6ˆkÑ	ó7ðz #ñ	à�c˜3�h‘ðð �c˜6�kÑ"ðð �t‘ð	ð
 
óð$/°ð /À$ó /ðb$#°Mð $#ÀdÈ3Áió $#ðL!°ð !À4ÈÁ9ó !ð6qà×-Ñ-ð6qð ˜d 3¨ 8™nÑ-ð6qð 
ó	6qðp ð)˜dÐ#9Ñ:¸TÑAò )ó ô)r%   r.   )*rb   rK   rp   Útypingr   r   r   r   r   rn   rm   ÚImportErrorrõ   rô   Úfastapir   r¢   r	   Úlitellm._loggingr
   Ú%litellm.integrations.custom_guardrailr   r   r   r‰   Ú&litellm.llms.custom_httpx.http_handlerr   r   Úlitellm.proxy._typesr   Ú'litellm.proxy.guardrails._content_utilsr   r   Úlitellm.types.guardrailsr   r   Ú3litellm.types.proxy.guardrails.guardrail_hooks.baser'   r¬   r)   r,   r.   r$   r%   r&   ú<module>r?     sÏ   ðó Û 	Û ß @Õ @ðÛà€Nð
Ûà€Oõ
 "ã Ý Ý 1÷õ E÷õ 0÷õ 9ô*�Iô *ñ ÝXô: 9ô :ôH˜Yô HôK)�_õ K)øðo ò Ø€DØ‚Nðûð ò Ø€EØ‚Oðús"   œB £B- Â	B*Â)B*Â-	B9Â8B9