Ë
    uPêjU±  ã                   óæ   — d Z ddlmZmZmZmZmZ ddlZddlm	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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m Z m!Z!m"Z" erddl#m$Z$ ddl%m&Z'  G d„ de«      Z(y)a;  
OpenAI Chat Completions Message Handler for Unified Guardrails

This module provides a class-based handler for OpenAI-format chat completions.
The class methods can be overridden for custom behavior.

Pattern Overview:
-----------------
1. Extract text content from messages/responses (both string and list formats)
2. Create async tasks to apply guardrails to each text segment
3. Track mappings to know where each response belongs
4. Apply guardrail responses back to the original structure

This pattern can be replicated for other message formats (e.g., Anthropic).
é    )ÚTYPE_CHECKINGÚAnyÚFinalÚUnionÚcastN)Úverbose_proxy_logger)ÚBaseTranslationÚStreamTransformSink)Ú.effective_scan_only_tool_results_for_guardrailÚ+effective_skip_system_message_for_guardrailÚ)effective_skip_tool_message_for_guardrailÚ!merge_guardrailed_scoped_messagesÚ'merge_returned_tools_into_request_toolsÚopenai_tool_nameÚrole_out_of_guardrail_scopeÚ!scoped_structured_message_indices)Ústream_chunk_builder)ÚAllMessageValuesÚChatCompletionToolParam)Úcoerce_stream_holdback_value)ÚChoicesÚGenericGuardrailAPIInputsÚModelResponseÚModelResponseStreamÚStreamingChoices)ÚCustomGuardrail)ÚLoggingc                   ó  — e Zd ZdZdedee   dz  fd„Z	 d6dedddd	defd
„Z	dedee
   fd„Z	 	 	 d7dee
ef   dedee
   dee
   dee   deeeedz  f      deeeef      dedededdfd„Zdeee
ef      dee
   deeeedz  f      ddfd„Zdeee
ef      deee
ef      deeeef      ddfd„Z	 	 	 d8dddddd	dedz  d edz  defd!„Z	 	 	 	 d9d"ed#   dddd	dedz  d edz  d$edz  ded#   fd%„Zd"ed#   dddd	dedz  d edz  ded#   fd&„Zed"ed#   deee
f   fd'„«       Zd"ed#   dddd	dedz  d edz  d(eddfd)„Zd"ed#   deeeedz  f   e
f   fd*„Zded+   defd,„Zd-eez  d.edee
   dee
   deee
ef      deeeedz  f      deeeef      ddfd/„Zd0ee
ef   ez  dee
ef   dz  fd1„Z dddee
   deeeedz  f      ddfd2„Z!dddeee
ef      deeeef      ddfd3„Z"ded#   d4ee
   deeeedz  f      ddfd5„Z#y):ÚOpenAIChatCompletionsHandlera*  
    Handler for processing OpenAI chat completions messages with guardrails.

    This class provides methods to:
    1. Process input messages (pre-call hook)
    2. Process output responses (post-call hook)

    Methods can be overridden to customize behavior for different message formats.
    ÚdataÚreturnNc                 óX   — |j                  d«      }|€yt        t        t           |«      S )z«
        Convert chat completions request data to OpenAI-spec structured messages.

        Messages are already in OpenAI format, so this is a simple extraction.
        ÚmessagesN)Úgetr   Úlistr   )Úselfr    r#   s      ú{/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/llms/openai/chat/guardrail_translation/handler.pyÚget_structured_messagesz4OpenAIChatCompletionsHandler.get_structured_messages@   s.   € ð Ÿ(™( :Ó.ˆØÐØÜ”DÔ)Ñ*¨HÓ5Ð5ó    Úguardrail_to_applyr   Úlitellm_logging_objzLiteLLMLoggingObj | Nonec              ƒ   óZ  K  — |j                  d«      }|€|S t        |«      }t        |«      }t        |«      }g }g }	g }
g }g }t	        |«      D ]   \  }}| j                  ||||	|
|||||¬«
       Œ" |s|
�rƒt        |¬«      }|	r|	|d<   |
r|
|d<   | j                  |«      }t        |xs g |||¬«      }|r|D �cg c]  }||   ‘Œ	 c}|d<   |j                  d	«      }|r|s||d	<   |j                  d
«      }|r||d
<   |j                  d«      }|j                  ||d|¬«      ƒ d{  –—† }|j                  dg «      }|j                  dg «      }|j                  d	«      }|�|rt        ||t        ¬«      n||d	<   |j                  d«      }|�,||ur(|j                  «       r|nt        |xs g ||¬«      |d<   n>|r|r| j                  |||¬«      ƒ d{  –—†  |r| j                  |||¬«      ƒ d{  –—†  t!        j"                  d|j                  d«      «       |S c c}w 7 Œý7 ŒP7 Œ4­w)zP
        Process input messages by applying guardrails to text content.
        r#   N)
ÚmessageÚmsg_idxÚtexts_to_checkÚimages_to_checkÚtool_calls_to_checkÚtext_task_mappingsÚtool_call_task_mappingsÚskip_system_messageÚskip_tool_messageÚscan_only_tool_results©ÚtextsÚimagesÚ
tool_calls)r6   Úskip_systemÚ	skip_toolÚstructured_messagesÚtoolsÚmodelÚrequest©ÚinputsÚrequest_dataÚ
input_typeÚlogging_objr8   )Úrequest_toolsÚreturned_toolsÚ	tool_name)Úfull_messagesÚscoped_indicesÚguardrailed_scoped)r#   Ú	responsesÚtask_mappings)r#   r:   rM   z5OpenAI Chat Completions: Processed input messages: %s)r$   r   r   r   Ú	enumerateÚ_extract_inputsr   r(   r   Úapply_guardrailr   r   Ú&structured_messages_cover_full_requestr   Ú)_apply_guardrail_responses_to_input_textsÚ._apply_guardrail_responses_to_input_tool_callsr   Údebug)r&   r    r*   r+   r#   r;   r<   r6   r/   r0   r1   r2   r3   r.   r-   rB   r=   Úscoped_message_indicesÚindexr>   r?   Úoriginal_structured_messagesÚguardrailed_inputsÚguardrailed_textsÚguardrailed_tool_callsÚguardrailed_toolsÚguardrailed_structured_messagess                              r'   Úprocess_input_messagesz3OpenAIChatCompletionsHandler.process_input_messagesK   së  è ø€ ð Ÿ(™( :Ó.ˆØÐØˆKäHÐI[Ó\ˆÜDÐEWÓXˆ	Ü(VÐWiÓ(jÐà+-ˆØ,.ˆØDFÐØBDÐØ@BÐô !*¨(Ó 3ò 	ÑˆG�WØ× Ñ ØØØ-Ø /Ø$7Ø#5Ø(?Ø$/Ø"+Ø'=ð !õ ð	ñ Ò0Ü5¸NÔKˆFÙØ#2��xÑ Ù"Ø':��|Ñ$Ø)-×)EÑ)EÀdÓ)KÐÜ,MØ#Ò) rØ'=Ø'Ø#ô	-Ð"ñ #ØYoÖ0pÐPUÐ1DÀUÓ1KÒ0p�Ð,Ñ-àŸ8™8 GÓ,ˆEÙÑ3Ø"'��w‘àŸ8™8 GÓ,ˆEÙØ"'��w‘à28·*±*Ð=RÓ2SÐ(Ø.@×.PÑ.PØØ!Ø$Ø/ð	 /Qó /÷ )Ðð (:×'=Ñ'=¸gÀrÓ'JÐØ,>×,BÑ,BÀ<ÐQSÓ,TÐ"Ø'9×'=Ñ'=¸gÓ'FÐØ Ð,ñ .ô <Ø&+Ø'8Ü"2õð +ð �W‘ð 6H×5KÑ5KÐLaÓ5bÐ+à/Ð;Ø3Ð;WÑWð *×PÑPÔRñ 4ä:Ø&9Ò&?¸RØ'=Ø+Jôð �ZÒ ñ %©Ø×HÑHØ!)Ø"3Ø&8ð Ió ÷ ð ñ *Ø×MÑMØ!)Ø#9Ø&=ð Nó ÷ ð ô 	×"Ñ"ØCØ�H‰H�ZÓ ô	
ð
 ˆùòI 1qð)øðLøðúsD   ‚B8H+Â:H ÃA!H+Ä'H%Ä(B.H+ÇH'ÇH+Ç4H)Ç51H+È'H+È)H+c                 óà  — g }|j                  d«      xs g D ]y  }t        |t        «      sŒ|j                  d«      dk(  sŒ)|j                  d«      }t        |t        «      sŒK|j                  d«      sŒ]|j                  t	        |d   «      «       Œ{ |j                  d«      xs g D ]B  }t        |t        «      sŒ|j                  d«      sŒ&|j                  t	        |d   «      «       ŒD |S )zbExtract tool names from OpenAI chat completions request (tools[].function.name, functions[].name).r>   ÚtypeÚfunctionÚnameÚ	functions)r$   Ú
isinstanceÚdictÚappendÚstr)r&   r    ÚnamesÚtoolÚfns        r'   Úextract_request_tool_namesz7OpenAIChatCompletionsHandler.extract_request_tool_namesÆ   sÂ   € à"$ˆØ—H‘H˜WÓ%Ò+¨ò 	2ˆDÜ˜$¤Õ%¨$¯(©(°6Ó*:¸jÓ*HØ—X‘X˜jÓ)�Ü˜b¤$Õ'¨B¯F©F°6­NØ—L‘L¤ R¨¡Z£Õ1ð		2ð
 —(‘(˜;Ó'Ò-¨2ò 	.ˆBÜ˜"œdÕ#¨¯©¨v­Ø—‘œS  F¡›_Õ-ð	.ð ˆr)   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   c                 óÜ  — t        t        |j                  d«      xs d«      j                  «       ||	|
¬«      ry|j                  dd«      }|��t	        |t        «      r%|j                  |«       |j                  |df«       nãt	        |t        «      rÓt        |«      D ]Å  \  }}|j                  dd«      }|�-|j                  |«       |j                  |t        |«      f«       |j                  d«      dk(  sŒ\|j                  di «      }t	        |t        «      r&|j                  d	«      }|sŒ’|j                  |«       Œ¤t	        |t        «      sŒµ|j                  |«       ŒÇ |j                  d
d«      }|�qt	        |t        «      r`t        |«      D ]Q  \  }}t	        |t        «      sŒ|j                  t        t        |«      «       |j                  |t        |«      f«       ŒS yyy)z 
        Extract text content, images, and tool calls from a message.

        Override this method to customize text/image/tool call extraction logic.
        ÚroleÚ )r4   r5   r6   NÚcontentÚtextr_   Ú	image_urlÚurlr:   )r   rf   r$   Úlowerrc   re   r%   rN   Úintrd   r   r   )r&   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   rn   Úcontent_idxÚcontent_itemÚtext_strrp   rq   r:   Útool_call_idxÚ	tool_calls                       r'   rO   z,OpenAIChatCompletionsHandler._extract_inputsÓ   s¿  € ô$ 'Ü�—‘˜FÓ#Ò) rÓ*×0Ñ0Ó2Ø 3Ø/Ø#9õ	
ð à Ÿ™ Y°Ó5ˆØÑÜ˜'¤3Ô'à×%Ñ% gÔ.Ø"×)Ñ)¨7°D¨/Õ:ä˜G¤TÔ*ä1:¸7Ó1Cò >Ñ-�K à+×/Ñ/°¸Ó=�HØÐ+Ø&×-Ñ-¨hÔ7Ø*×1Ñ1°7¼CÀÓ<LÐ2MÔNð $×'Ñ'¨Ó/°;Ó>Ø$0×$4Ñ$4°[À"Ó$E˜	Ü% i´Ô6Ø"+§-¡-°Ó"6˜CÚ"Ø /× 6Ñ 6°sÕ ;Ü'¨	´3Õ7Ø+×2Ñ2°9Õ=ð>ð$ $ŸK™K¨°dÓ;ˆ
ØÐ!¤j°¼TÔ&BÜ,5°jÓ,Aò RÑ(�˜yÜ˜i¬Õ.à'×.Ñ.¬tÔ4KÈYÓ/WÔXØ+×2Ñ2°G¼SÀÓ=OÐ3PÕQñ	Rð 'CÐ!r)   r#   rL   rM   c              ƒ   ó8  K  — t        |«      D ]ˆ  \  }}||   }t        t        |d   «      }t        t        dz  |d   «      }||   j                  dd«      }	|	€ŒLt	        |	t
        «      r|€	|||   d<   Œgt	        |	t        «      sŒx|€Œ{|||   d   |   d<   ŒŠ y­w)zš
        Apply guardrail responses back to input message text content.

        Override this method to customize how text responses are applied.
        r   Né   rn   ro   )rN   r   rs   r$   rc   rf   r%   )
r&   r#   rL   rM   Útask_idxÚguardrail_responseÚmappingr.   Úcontent_idx_optionalrn   s
             r'   rR   zFOpenAIChatCompletionsHandler._apply_guardrail_responses_to_input_texts  s½   è ø€ ô -6°iÓ,@ò 	`Ñ(ˆHÐ(Ø# HÑ-ˆGÜœ3 ¨¡
Ó+ˆGÜ#'¬¨d©
°G¸A±JÓ#?Ð ð ˜wÑ'×+Ñ+¨I°tÓ<ˆGØˆØä˜'¤3Ô'Ð,@Ð,Hà/A�˜Ñ! )Ò,ä˜G¤TÕ*Ð/CÑ/OàM_�˜Ñ! )Ñ,Ð-AÑBÀ6ÒJñ!	`ùs   ‚BBÂBÂ	Br:   c              ƒ   óÚ   K  — t        |«      D ]Y  \  }\  }}|t        |«      k  sŒ||   }||   j                  dd«      }|€Œ5t        |t        «      sŒF|t        |«      k  sŒU|||<   Œ[ y­w)a(  
        Apply guardrailed tool calls back to input messages.

        The guardrail may have modified the tool_calls list in place,
        so we apply the modified tool calls back to the original messages.

        Override this method to customize how tool call responses are applied.
        r:   N)rN   Úlenr$   rc   r%   )	r&   r#   r:   rM   r{   r.   rw   Úguardrailed_tool_callÚmessage_tool_callss	            r'   rS   zKOpenAIChatCompletionsHandler._apply_guardrail_responses_to_input_tool_calls-  s�   è ø€ ô 3<¸MÓ2Jò 	RÑ.ˆHÑ.�w Øœ#˜j›/Ó)Ø(2°8Ñ(<Ð%Ø%-¨gÑ%6×%:Ñ%:¸<ÈÓ%NÐ"Ø%Ñ1´jÐASÔUYÕ6ZØ$¤sÐ+=Ó'>Ó>à<QÐ*¨=Ò9ñ	Rùs   ‚#A+¦A+ÁA+ÁA+Á#A+Úresponser   Úuser_api_key_dictrC   c           
   ƒ   óŽ  K  — | j                  |«      st        j                  d«       |S g }g }g }g }	g }
t        |j                  «      D ]  \  }}| j                  ||||||	|
¬«       Œ |s|�r8|€d|i}n	d|vr||d<   d|vr| j                  |«      }|r||d<   t        |¬«      }|r||d<   |r||d<   t        |d	«      r|j                  r|j                  |d	<   |j                  ||d|¬
«      ƒ d{  –—† }|j                  dg «      }|j                  d«      }t        |t        «      r;t        |«      t        |«      k(  r$t        t        t         t"        t$        f      |«      n|}|r|r| j'                  |||	¬«      ƒ d{  –—†  |r| j)                  |||
¬«      ƒ d{  –—†  t        j*                  d|«       |S 7 ŒÊ7 Œ<7 Œ ­w)ap  
        Process output response by applying guardrails to text content.

        Args:
            response: LiteLLM ModelResponse object
            guardrail_to_apply: The guardrail instance to apply
            litellm_logging_obj: Optional logging object
            user_api_key_dict: User API key metadata to pass to guardrails

        Returns:
            Modified response with guardrail applied to content

        Response Format Support:
            - String content: choice.message.content = "text here"
            - List content: choice.message.content = [{"type": "text", "text": "text here"}, ...]
        zHOpenAI Chat Completions: No text content in response, skipping guardrail)ÚchoiceÚ
choice_idxr/   r0   r1   r2   r3   Nrƒ   Úlitellm_metadatar7   r9   r:   r?   rA   r8   )rƒ   rL   rM   )rƒ   r:   rM   z6OpenAI Chat Completions: Processed output response: %s)Ú_has_text_contentr   ÚwarningrN   ÚchoicesÚ*_extract_output_text_images_and_tool_callsÚ'transform_user_api_key_dict_to_metadatar   Úhasattrr?   rP   r$   rc   r%   r€   r   rd   rf   r   Ú*_apply_guardrail_responses_to_output_textsÚ/_apply_guardrail_responses_to_output_tool_callsrT   )r&   rƒ   r*   r+   r„   rC   r/   r0   r1   r2   r3   r‡   r†   Úuser_metadatarB   rX   rY   Úreturned_tool_callsrZ   s                      r'   Úprocess_output_responsez4OpenAIChatCompletionsHandler.process_output_responseD  s;  è ø€ ð4 ×%Ñ% hÔ/Ü ×(Ñ(Ð)sÔtØˆOà+-ˆØ,.ˆØ;=ÐØBDÐØ@BÐô #,¨H×,<Ñ,<Ó"=ò 		ÑˆJ˜Ø×;Ñ;ØØ%Ø-Ø /Ø$7Ø#5Ø(?ð <õ ð		ñ Ò0ð Ð#Ø *¨HÐ5‘à \Ñ1Ø/7�L Ñ,ð "¨Ñ5Ø'+×'SÑ'SÐTeÓ'f�Ù Ø7D�LÐ!3Ñ4ä5¸NÔKˆFÙØ#2��xÑ Ù"Ø':��|Ñ$ä�x Ô)¨h¯nªnØ"*§.¡.��w‘à.@×.PÑ.PØØ)Ø%Ø/ð	 /Qó /÷ )Ðð (:×'=Ñ'=¸gÀrÓ'JÐØ);×)?Ñ)?ÀÓ)MÐô Ð1´4Ô8¼SÐATÓ=UÔY\Ð]pÓYqÒ=qô ”Tœ$œs¤C˜x™.Ñ)Ð+>Ô?à(ð #ñ !¡^Ø×EÑEØ%Ø/Ø"4ð Fó ÷ ð ñ &Ø×JÑJØ%Ø5Ø"9ð Kó ÷ ð ô 	×"Ñ"Ð#[Ð]eÔfàˆðC)øð"øðús7   ‚C2GÃ4F?Ã5BGÆGÆGÆ"GÆ#GÇGÇGÚresponses_so_farr   Ústream_transform_sinkc              ƒ   ó”   K  — |�!| j                  ||||||¬«      ƒ d{  –—†  |S | j                  |||||¬«      ƒ d{  –—† S 7 Œ$7 Œ­w)a  
        Process output streaming responses by applying guardrails to text content.

        Args:
            responses_so_far: List of LiteLLM ModelResponseStream objects
            guardrail_to_apply: The guardrail instance to apply
            litellm_logging_obj: Optional logging object
            user_api_key_dict: User API key metadata to pass to guardrails
            stream_transform_sink: Optional out-parameter for the streaming text
                transformation path. When provided, the guardrail runs over the raw
                accumulated text (``responses_so_far`` is left untouched so it stays
                a correct raw accumulator across rounds) and the guardrailed text
                plus requested holdback are reported per choice on the sink.

        Returns:
            The (unmodified) list of responses.

        Response Format Support:
            - String content: choice.message.content = "text here"
            - List content: choice.message.content = [{"type": "text", "text": "text here"}, ...]
        N)r”   r*   r+   r„   rC   Úsink)r”   r*   r+   r„   rC   )Ú_process_streaming_transformÚ_process_streaming_block_only)r&   r”   r*   r+   r„   rC   r•   s          r'   Ú!process_output_streaming_responsez>OpenAIChatCompletionsHandler.process_output_streaming_response³  s|   è ø€ ð< !Ð,Ø×3Ñ3Ø!1Ø#5Ø$7Ø"3Ø)Ø*ð 4ó ÷ ð ð $Ð#à×7Ñ7Ø-Ø1Ø 3Ø/Ø%ð 8ó 
÷ 
ð 	
ðøð
ús    ‚AŸA A¿AÁ AÁAc             ƒ   ó¾  K  — d}|D ],  }|j                   sŒ|j                   d   j                  €Œ*d} n |r;t        t        t	        ||¬«      «      }| j                  |||||¬«      ƒ d{  –—†  |S d}	|D ]  }
| j                  |
«      sŒd}	 n |	st        j                  d«       |S | j                  |«      }g }g }g }|j                  «       D ],  \  \  }}}|j                  |«       |j                  ||f«       Œ. |r¼|€d|i}n	d|vr||d<   d	|vr| j                  |«      }|r||d	<   t        |¬
«      }|r||d<   |r0t        |d   d«      r!|d   j                  r|d   j                  |d<   |j!                  ||d|¬«      ƒ d{  –—† }|j#                  dg «      }| j%                  |||¬«      ƒ d{  –—†  t        j&                  d|«       |S 7 �Œj7 ŒM7 Œ!­w)zæBlock-only streaming path: run the guardrail so an in-flight BLOCK can
        terminate the stream. Text rewrites are not propagated to the client here
        (see ``_process_streaming_transform`` for the incremental_diff path).Fr   NT)ÚchunksrE   )rƒ   r*   r+   r„   rC   zSOpenAI Chat Completions: No text content in streaming responses, skipping guardrailrL   rˆ   r7   r9   r?   rƒ   rA   r8   )rL   rY   rM   zAOpenAI Chat Completions: Processed output streaming responses: %s)r‹   Úfinish_reasonr   r   r   r“   r‰   r   rŠ   Ú_combine_streaming_textsÚitemsre   r�   r   rŽ   r?   rP   r$   Ú._apply_guardrail_responses_to_output_streamingrT   )r&   r”   r*   r+   r„   rC   Úhas_stream_endedÚchunkÚmodel_responseÚhas_any_text_contentrƒ   Úcombined_textsr/   r0   rM   Úmap_choice_idxÚmap_content_idxÚcombined_textr‘   rB   rX   rY   s                         r'   r™   z:OpenAIChatCompletionsHandler._process_streaming_block_onlyä  s„  è ø€ ð !ÐØ%ò 	ˆEØ�}‹} §¡¨qÑ!1×!?Ñ!?Ñ!KØ#'Ð Ùð	ñ
 ä$(ÜÜ$Ð,<ÐJ]Ô^ó%ˆNð
 ×.Ñ.Ø'Ø#5Ø$7Ø"3Ø)ð /ó ÷ ð ð $Ð#ð  %ÐØ(ò 	ˆHØ×%Ñ% hÕ/Ø'+Ð$Ùð	ñ
 $Ü ×(Ñ(Øeôð $Ð#ð
 !%× =Ñ =Ð>NÓ Oˆð ,.ˆØ,.ˆØ=?ˆð AO×@TÑ@TÓ@Vò 	DÑ<Ñ-ˆ^˜_¨}Ø×!Ñ! -Ô0Ø× Ñ  .°/Ð!BÕCð	Dñ
 ð Ð#Ø +Ð-=Ð>‘à lÑ2Ø0@�L Ñ-ð "¨Ñ5Ø'+×'SÑ'SÐTeÓ'f�Ù Ø7D�LÐ!3Ñ4ä5¸NÔKˆFÙØ#2��xÑ á¤GÐ,<¸QÑ,?ÀÔ$IÐN^Ð_`ÑNa×NgÒNgØ"2°1Ñ"5×";Ñ";��w‘Ø.@×.PÑ.PØØ)Ø%Ø/ð	 /Qó /÷ )Ðð (:×'=Ñ'=¸gÀrÓ'JÐð ×EÑEØ*Ø"3Ø+ð Fó ÷ ð ô 	×"Ñ"ØOØô	
ð
  Ððeùðx)øðúsF   ‚G—G±;GÁ,GÁ-GÂC?GÆGÆ-GÆ9GÆ:GÇGÇGc                 óz  — i }| D ]³  }|j                   D ]¢  }t        |t        j                  «      r|j                  j
                  }n2t        |t        j                  «      r|j                  j
                  }nŒft        |t        «      sŒw|sŒzt        |dd«      xs d}|j                  |d«      |z   ||<   Œ¤ Œµ |S )aÑ  Accumulate raw string ``delta.content`` per choice, keyed by
        ``StreamingChoices.index`` (not enumerate position, which collapses to 0
        when each chunk carries a single non-zero-indexed choice for ``n > 1``).

        Only string content participates; list-of-blocks content is out of scope
        for the incremental transform path. Reads ``responses_so_far`` without
        mutating it so it stays a correct raw accumulator across rounds.
        rV   r   rm   )r‹   rc   Úlitellmr   Údeltarn   r   r-   rf   Úgetattrr$   )r”   Úaccumulatedrƒ   r†   rn   Úidxs         r'   Ú*_accumulate_string_content_by_choice_indexzGOpenAIChatCompletionsHandler._accumulate_string_content_by_choice_indexR  s±   € ð .0ˆØ(ò 
	JˆHØ"×*Ñ*ò 	J�Ü˜f¤g×&>Ñ&>Ô?Ø$Ÿl™l×2Ñ2‘GÜ ¬¯©Ô8Ø$Ÿn™n×4Ñ4‘GàÜ˜g¤sÕ+²Ü! &¨'°1Ó5Ò:¸�CØ'2§¡°s¸BÓ'?À'Ñ'I�K Ò$ñ	Jð
	Jð Ðr)   r—   c             ƒ   óŠ  K  — | j                  |«      }|si |_        i |_        yt        |j	                  «       «      }|D �	cg c]  }	||	   ‘Œ	 }
}	|€d|i}n	d|vr||d<   d|vr| j                  |«      }|r||d<   t        |
¬«      }|r"t        |d   dd«      r|d   j                  |d<   |j                  ||d|¬«      ƒ d{  –—† }|j                  d	«      }|€|
}n@t        |«      t        |
«      k  r)t        j                  d
t        |«      t        |
«      «       |j                  d«      xs g }t        |«      D �	�ci c]  \  }	}|	t        |«      k  sŒ|||	   “Œ c}}	|_        t        t        |«      «      D �	ci c]#  }	|	t        |«      k  sŒ||	   t!        ||	   «      “Œ% c}	|_        yc c}	w 7 Œñc c}}	w c c}	w ­w)a}  Run the guardrail over the raw accumulated text and report the
        guardrailed text plus requested holdback per choice on ``sink``.

        Unlike the block-only path this never mutates ``responses_so_far``: it
        re-derives the raw accumulated text every round (so a rewrite guardrail
        always sees consistent input) and hands the result back out of band.
        NrL   rˆ   r7   r   r?   rƒ   rA   r8   z¡OpenAI Chat Completions: guardrail returned %s transformed texts for %s inputs on the streaming transform path; withholding the unmatched choices to fail closed.Ústream_holdback_chars)r¯   Úmutated_text_per_choiceÚholdback_per_choiceÚsortedÚkeysr�   r   r¬   r?   rP   r$   r€   r   rŠ   rN   Úranger   )r&   r”   r*   r+   r„   rC   r—   Úraw_by_indexÚindicesÚir/   r‘   rB   rX   Úreturned_textsÚholdbackr®   s                    r'   r˜   z9OpenAIChatCompletionsHandler._process_streaming_transforml  s  è ø€ ð" #×MÑMÐN^Ó_ˆÙØ+-ˆDÔ(Ø')ˆDÔ$Øô   × 1Ñ 1Ó 3Ó4ˆØ:AÖ B°Q ¨a£Ð BˆÐ BàÐØ'Ð)9Ð:‰LØ Ñ,Ø(8ˆL˜Ñ%Ø \Ñ1Ø#'×#OÑ#OÐPaÓ#bˆMÙØ3@�Ð/Ñ0ä1¸ÔGˆÙ¤Ð(8¸Ñ(;¸WÀdÔ KØ.¨qÑ1×7Ñ7ˆF�7‰OØ*<×*LÑ*LØØ%Ø!Ø+ð	 +Mó +
÷ %
Ðð ,×/Ñ/°Ó8ˆð
 Ð!Ø+‰NÜ�Ó ¤3 ~Ó#6Ò6Ü ×(Ñ(ð^ä�NÓ#Ü�NÓ#ô	ð -×0Ñ0Ð1HÓIÒOÈRˆä1:¸7Ó1C÷(
Ù'- q¨#ÀqÌ3È~ÓK^ÓG^ˆC� Ñ"Ñ"ó(
ˆÔ$ô LQÔQTÐU\ÓQ]ÓK^ö$
ØFGÐbcÔfiÐjrÓfsÓbsˆG�A‰JÔ4°X¸a±[ÓAÑAò$
ˆÕ ùòQ !Cð%
úó.(
ùò$
ùsJ   ‚A GÁF1ÁA6GÃF6ÃA=GÅF8ÅF8Å!GÆ F>ÆF>Æ(GÆ8Gc                 ó4  — i }t        |«      D �]  \  }}t        |j                  «      D ]è  \  }}t        |t        j                  «      r|j
                  j                  }n2t        |t        j                  «      r|j                  j                  }nŒi|€Œlt        |t        «      r|df}||vrd||<   ||xx   |z  cc<   Œ—t        |t        «      sŒ¨t        |«      D ]3  \  }	}
|
j                  d«      }|sŒ||	f}||vrd||<   ||xx   |z  cc<   Œ5 Œê �Œ	 |S )aW  
        Combine all streaming chunks into complete text per choice.

        For streaming, we need to concatenate all delta.content across all chunks.

        Args:
            responses_so_far: List of LiteLLM ModelResponseStream objects

        Returns:
            Dict mapping (choice_idx, content_idx) to combined text string
        Nrm   ro   )rN   r‹   rc   rª   r   r«   rn   r   r-   rf   r%   r$   )r&   r”   r¥   Úresponse_idxrƒ   r‡   r†   rn   Ústr_keyrt   ru   rv   Úlist_keys                r'   rž   z5OpenAIChatCompletionsHandler._combine_streaming_texts¶  s8  € ð DFˆä&/Ð0@Ó&Aó 	AÑ"ˆL˜(Ü&/°×0@Ñ0@Ó&Aò AÑ"�
˜FÜ˜f¤g×&>Ñ&>Ô?Ø$Ÿl™l×2Ñ2‘GÜ ¬¯©Ô8Ø$Ÿn™n×4Ñ4‘Gàà�?Øä˜g¤sÔ+à7AÀ4Ð6H�GØ nÑ4Ø24˜ wÑ/Ø" 7Ó+¨wÑ6Ô+ä ¬Õ.ä5>¸wÓ5Gò 	AÑ1˜ \Ø#/×#3Ñ#3°FÓ#;˜Ú#à *Ø +ð@˜Hð  (¨~Ñ=Ø;= ¨xÑ 8Ø*¨8Ó4¸Ñ@Ô4ñ	Aò)Að	Að@ Ðr)   ©r   r   c                 óf  — ddl m}m} t        ||«      rÉ|j                  D ]¹  }t        |t
        j                  «      sŒ|j                  j                  r&t        |j                  j                  t        «      r y|j                  j                  sŒqt        |j                  j                  t        «      sŒ–t        |j                  j                  «      dkD  sŒ¹ y yt        ||«      rÈ|j                  D ]¹  }t        |t
        j                  «      sŒ|j                  j                  r&t        |j                  j                  t        «      r y|j                  j                  sŒqt        |j                  j                  t        «      sŒ–t        |j                  j                  «      dkD  sŒ¹ y y)z•
        Check if response has any text content or tool calls to process.

        Override this method to customize text content detection.
        r   rÀ   TF)Úlitellm.types.utilsr   r   rc   r‹   rª   r   r-   rn   rf   r:   r%   r€   r   r«   )r&   rƒ   r   r   r†   Ústreaming_choices         r'   r‰   z.OpenAIChatCompletionsHandler._has_text_contentè  s>  € ÷ 	Kä�h Ô.Ø"×*Ñ*ò (�Ü˜f¤g§o¡oÕ6à—~‘~×-Ò-´*¸V¿^¹^×=SÑ=SÔUXÔ2YÙ#à—~‘~×0Ó0´ZÀÇÁ×@YÑ@YÔ[_Õ5`Ü˜vŸ~™~×8Ñ8Ó9¸AÓ=Ù#'ð(ð& ô ˜Ð"5Ô6Ø$,×$4Ñ$4ò (Ð ÜÐ.´×0HÑ0HÕIà'×-Ñ-×5Ò5¼*ÐEU×E[ÑE[×EcÑEcÔehÔ:iÙ#à'×-Ñ-×8Ó8¼ZÐHX×H^ÑH^×HiÑHiÔkoÕ=pÜÐ/×5Ñ5×@Ñ@ÓAÀAÓEÙ#'ð(ð r)   r†   r‡   c                 ó  — t        j                  d|«       d}d}	t        |t        j                  «      r-|j
                  j                  }|j
                  j                  }	nHt        |t        j                  «      r-|j                  j                  }|j                  j                  }	ny|r5t        |t        «      r%|j                  |«       |j                  |df«       nÂ|rÀt        |t        «      r°t        |«      D ]¢  \  }
}|j                  d«      }|r-|j                  |«       |j                  |t        |
«      f«       |j                  d«      dk(  sŒ[|j                  di «      }t        |t         «      sŒ~|j                  d«      }|sŒ’|j                  |«       Œ¤ |	�ft        |	t        «      rUt        |	«      D ]F  \  }}| j#                  |«      }|sŒ|j                  |«       |j                  |t        |«      f«       ŒH yyy)z¨
        Extract text content, images, and tool calls from a response choice.

        Override this method to customize text/image/tool call extraction logic.
        z.OpenAI Chat Completions: Processing choice: %sNro   r_   rp   rq   )r   rT   rc   rª   r   r-   rn   r:   r   r«   rf   re   r%   rN   r$   rs   rd   Ú_convert_tool_call_to_dict)r&   r†   r‡   r/   r0   r1   r2   r3   rn   r:   rt   ru   Úcontent_textrp   rq   rw   rx   Útool_call_dicts                     r'   rŒ   zGOpenAIChatCompletionsHandler._extract_output_text_images_and_tool_calls  sÌ  € ô 	×"Ñ"Ð#SÐU[Ô\ð ˆØ'+ˆ
Ü�fœgŸo™oÔ.Ø—n‘n×,Ñ,ˆGØŸ™×2Ñ2‰JÜ˜¤× 8Ñ 8Ô9Ø—l‘l×*Ñ*ˆGØŸ™×0Ñ0‰Jð ñ ”z '¬3Ô/à×!Ñ! 'Ô*Ø×%Ñ% z°4Ð&8Õ9áœ G¬TÔ2ä-6°wÓ-?ò 8Ñ)�˜\à+×/Ñ/°Ó7�ÙØ"×)Ñ)¨,Ô7Ø&×-Ñ-¨z¼3¸{Ó;KÐ.LÔMð  ×#Ñ# FÓ+¨{Ó:Ø ,× 0Ñ 0°¸bÓ A�IÜ! )¬TÕ2Ø'Ÿm™m¨EÓ2˜ÚØ+×2Ñ2°3Õ7ð8ð  Ð!¤j°¼TÔ&BÜ,5°jÓ,Aò UÑ(�˜yà!%×!@Ñ!@ÀÓ!K�Ú!Ø'×.Ñ.¨~Ô>Ø+×2Ñ2°JÄÀMÓ@RÐ3SÕTñUð 'CÐ!r)   rx   c                 óP  — t        |t        «      r|S t        |d«      rˆt        |d«      r||j                  }i }t        |d«      r|j                  |d<   t        |d«      r|j
                  |d<   t        |d«      r|j                  ndt        |d«      r|j                  nd|dœ}|S y)zŠ
        Convert a tool call object to dictionary format.

        Tool calls can be either dict or object depending on the type.
        Úidr`   ra   Ú	argumentsNr_   )rÉ   r_   r`   )rc   rd   rŽ   r`   ra   rÊ   rÉ   r_   )r&   rx   r`   Úfunction_dictrÇ   s        r'   rÅ   z7OpenAIChatCompletionsHandler._convert_tool_call_to_dictD  s¤   € ô �i¤Ô&ØÐÜ�Y Ô%¬'°)¸ZÔ*Hà'×0Ñ0ˆHØ#%ˆMÜ�x Ô(Ø(0¯©�˜fÑ%Ü�x Ô-Ø-5×-?Ñ-?�˜kÑ*ô '.¨i¸Ô&>�i—l’lÀDÜ*1°)¸VÔ*D˜	ŸšÈ*Ø)ñ%ˆNð
 "Ð!Ør)   c              ƒ   óz  K  — t        |«      D ]©  \  }}||   }t        t        |d   «      }t        t        dz  |d   «      }t        t        |j                  |   «      }	|	j
                  j                  }
|
€Œjt        |
t        «      r|€||	j
                  _        ŒŽt        |
t        «      sŒŸ|€Œ¢||
|   d<   Œ« y­w)z”
        Apply guardrail text responses back to output response.

        Override this method to customize how text responses are applied.
        r   Nrz   ro   )
rN   r   rs   r   r‹   r-   rn   rc   rf   r%   )r&   rƒ   rL   rM   r{   r|   r}   r‡   r~   r†   rn   s              r'   r�   zGOpenAIChatCompletionsHandler._apply_guardrail_responses_to_output_texts]  s¿   è ø€ ô -6°iÓ,@ò 	KÑ(ˆHÐ(Ø# HÑ-ˆGÜœc 7¨1¡:Ó.ˆJÜ#'¬¨d©
°G¸A±JÓ#?Ð äœ' 8×#3Ñ#3°JÑ#?Ó@ˆFð —n‘n×,Ñ,ˆGØˆØä˜'¤3Ô'Ð,@Ð,Hà);�—‘Õ&ä˜G¤TÕ*Ð/CÑ/Oà8J�Ð,Ñ-¨fÒ5ñ%	Kùs   ‚B*B;Â-B;Â0B;c              ƒ   óŒ  K  — t        |«      D ]²  \  }\  }}|t        |«      k  sŒ||   }t        t        |j                  |   «      }|j
                  j                  }	|	€ŒSt        |	t        «      sŒd|t        |	«      k  sŒs|	|   }
d|v sŒ}|d   }d|v r|d   |
j                  _
        d|v sŒŸ|d   |
j                  _        Œ´ y­w)aO  
        Apply guardrailed tool calls back to the output response.

        The guardrail may return updated tool calls (either mutated in place or as
        a new list), so we apply the provided tool calls back to the original
        response.

        Override this method to customize how tool call responses are applied.
        Nr`   rÊ   ra   )rN   r€   r   r   r‹   r-   r:   rc   r%   r`   rÊ   ra   )r&   rƒ   r:   rM   r{   r‡   rw   r�   r†   Úchoice_tool_callsÚexisting_tool_callÚ	func_dicts               r'   r�   zLOpenAIChatCompletionsHandler._apply_guardrail_responses_to_output_tool_calls|  sÜ   è ø€ ô 6?¸}Ó5Mò 	UÑ1ˆHÑ1�z =Øœ#˜j›/Ó)Ø(2°8Ñ(<Ð%Üœg x×'7Ñ'7¸
Ñ'CÓD�Ø$*§N¡N×$=Ñ$=Ð!à$Ñ0´ZÐ@QÔSWÕ5XØ$¤sÐ+<Ó'=Ó=à->¸}Ñ-MÐ*à%Ð)>Ò>Ø(=¸jÑ(I˜IØ*¨iÑ7ØHQÐR]ÑH^Ð 2× ;Ñ ;Ô EØ%¨Ò2ØCLÈVÑCTÐ 2× ;Ñ ;Õ @ñ!	Uùs(   ‚#C¦:CÁ!CÁ2CÂ	CÂ!CÂ-CrY   c              ƒ   ó$  K  — i }t        |«      D ]:  \  }}||   }t        t        |d   «      }t        t        dz  |d   «      }	||||	f<   Œ< i }
t        |«      D �]±  \  }}t        |j                  «      D �]’  \  }}t	        |t
        j                  «      r|j                  j                  }n2t	        |t
        j                  «      r|j                  j                  }nŒj|€Œmt	        |t        «      rÌ|df}||v sŒ†||
vrct	        |t
        j                  «      r||   |j                  _        n.t	        |t
        j                  «      r||   |j                  _        d|
|<   Œít	        |t
        j                  «      rd|j                  _        �Œt	        |t
        j                  «      s�Œ6d|j                  _        �ŒIt	        |t        «      s�Œ[t        |«      D ]*  \  }}d|v sŒ||f}||v sŒ||
vr||   |d<   d|
|<   Œ&d|d<   Œ, �Œ• �Œ´ y­w)aN  
        Apply guardrail responses back to output streaming responses.

        For streaming responses, the guardrailed text (which is the combined text from all chunks)
        is placed in the first chunk, and subsequent chunks are cleared.

        Args:
            responses: List of ModelResponseStream objects to modify
            guardrailed_texts: List of guardrailed text responses (combined from all chunks)
            task_mappings: List of tuples (choice_idx, content_idx)

        Override this method to customize how responses are applied to streaming responses.
        r   Nrz   Trm   ro   )rN   r   rs   r‹   rc   rª   r   r«   rn   r   r-   rf   r%   )r&   rL   rY   rM   Úguardrail_mapr{   r|   r}   r‡   r~   Úalready_setr½   rƒ   Úchoice_idx_in_responser†   rn   r¾   rt   ru   r¿   s                       r'   r    zKOpenAIChatCompletionsHandler._apply_guardrail_responses_to_output_streaming�  s  è ø€ ð( CEˆÜ,5Ð6GÓ,Hò 	SÑ(ˆHÐ(Ø# HÑ-ˆGÜœc 7¨1¡:Ó.ˆJÜ#'¬¨d©
°G¸A±JÓ#?Ð Ø@RˆM˜:Ð';Ð<Ò=ð		Sð BDˆô '0°	Ó&:ó -	>Ñ"ˆL˜(Ü2;¸H×<LÑ<LÓ2Mó ,>Ñ.Ð&¨Ü˜f¤g×&>Ñ&>Ô?Ø$Ÿl™l×2Ñ2‘GÜ ¬¯©Ô8Ø$Ÿn™n×4Ñ4‘Gàà�?Øä˜g¤sÔ+à7MÈtÐ6T�GØ -Ò/Ø"¨+Ñ5ä)¨&´'×2JÑ2JÔKØ7DÀWÑ7M §¡Õ 4Ü!+¨F´G·O±OÔ!DØ9FÀwÑ9O §¡Ô 6Ø37˜K¨Ò0ô  *¨&´'×2JÑ2JÔKØ79 §¡Ö 4Ü!+¨F´G·O±OÖ!DØ9; §¡Ö 6ä ¬Ö.ä5>¸wÓ5Gò >Ñ1˜ \Ø! \Ò1à 6Ø +ð@˜Hð  (¨=Ò8Ø#+°;Ñ#>à;HÈÑ;R L°Ñ$8Ø<@ K°Ò$9ð <> L°Ò$8ò>ò?,>ñ-	>ùs%   ‚C9HÃ<B.HÆ,#HÇHÇ'HÇ0 H)N)FFF)NNN)NNNN)$Ú__name__Ú
__module__Ú__qualname__Ú__doc__rd   r%   r   r(   r   r]   rf   rj   rs   r   ÚtupleÚboolrO   rR   rS   r“   r
   rš   r™   Ústaticmethodr¯   r˜   rž   r   r‰   r   r   rŒ   rÅ   r�   r�   r    © r)   r'   r   r   5   sˆ  „ ñð	6¨Dð 	6°TÐ:JÑ5KÈdÑ5Ró 	6ð ;?ñ	yàðyð .ðyð 8ð	yð
 
óyðv¨tð ¸¸S¹	ó ð, %*Ø"'Ø',ñ;Rà�c˜3�h‘ð;Rð ð;Rð ˜S™	ð	;Rð
 ˜c™ð;Rð "Ð"9Ñ:ð;Rð !  s¨C°$©J Ñ!7Ñ8ð;Rð "& e¨C°¨H¡oÑ!6ð;Rð "ð;Rð  ð;Rð !%ð;Rð 
ó;Rðz`à�t˜C ˜H‘~Ñ&ð`ð ˜‘9ð`ð ˜E # s¨T¡z /Ñ2Ñ3ð	`ð
 
ó`ð:Rà�t˜C ˜H‘~Ñ&ðRð ˜˜c 3˜h™Ñ(ðRð ˜E # s (™OÑ,ð	Rð
 
óRð6 ;?Ø(,Ø$(ñmà!ðmð .ðmð 8ð	mð
  ™:ðmð ˜T‘kðmð 
ómðf ;?Ø(,Ø$(Ø<@ñ/
àÐ4Ñ5ð/
ð .ð/
ð 8ð	/
ð
  ™:ð/
ð ˜T‘kð/
ð  3°TÑ9ð/
ð 
Ð#Ñ	$ó/
ðbl ð Ð4Ñ5ðl ð .ð	l ð
 8ðl ð  ™:ðl ð ˜T‘kðl ð 
Ð#Ñ	$ól ð\ ðØÐ4Ñ5ðà	ˆc�3ˆh‰òó ðð2H
ð Ð4Ñ5ðH
ð .ð	H
ð
 8ðH
ð  ™:ðH
ð ˜T‘kðH
ð "ðH
ð 
óH
ðT0Ø $Ð%:Ñ ;ð0à	ˆe�C˜˜t™�OÑ$ cÐ)Ñ	*ó0ðd¨%Ð0VÑ*Wð Ð\`ó ð<<UàÐ*Ñ*ð<Uð ð<Uð ˜S™	ð	<Uð
 ˜c™ð<Uð " $ s¨C x¡.Ñ1ð<Uð !  s¨C°$©J Ñ!7Ñ8ð<Uð "& e¨C°¨H¡oÑ!6ð<Uð 
ó<Uð|°D¸¸c¸±NÀSÑ4Hð ÈTÐRUÐWZÐRZÉ^Ð^bÑMbó ð2Kà!ðKð ˜‘9ðKð ˜E # s¨T¡z /Ñ2Ñ3ð	Kð
 
óKð>Uà!ðUð ˜˜c 3˜h™Ñ(ðUð ˜E # s (™OÑ,ð	Uð
 
óUðBM>àÐ-Ñ.ðM>ð   ™9ðM>ð ˜E # s¨T¡z /Ñ2Ñ3ð	M>ð
 
ôM>r)   r   ))rØ   Útypingr   r   r   r   r   rª   Úlitellm._loggingr   Ú<litellm.llms.base_llm.guardrail_translation.base_translationr	   r
   Ú1litellm.llms.base_llm.guardrail_translation.utilsr   r   r   r   r   r   r   r   Úlitellm.mainr   Úlitellm.types.llms.openair   r   ÚDlitellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_apir   rÂ   r   r   r   r   r   Ú%litellm.integrations.custom_guardrailr   Ú*litellm.litellm_core_utils.litellm_loggingr   ÚLiteLLMLoggingObjr   rÜ   r)   r'   ú<module>rç      s\   ðñ÷  :Õ 9ã Ý 1÷÷	÷ 	ó 	õ .ß Oõ÷õ ñ ÝEÝWôu> ?õ u>r)   