o
    ưio                     @   s   d Z ddlmZmZmZmZmZmZmZ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 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 deZ$G dd deZ%dS )z
Transformation logic from OpenAI /v1/chat/completion format to Mistral's /chat/completion format.

Why separate file? Make it easy to see how transformation works

Docs - https://docs.mistral.ai/api/
    )AnyAsyncIterator	CoroutineIteratorListLiteralOptionalTupleUnioncastget_type_hintsoverloadN)Logging)3handle_messages_with_content_list_to_str_conversionstrip_none_values_from_message)OpenAIGPTConfig$OpenAIChatCompletionStreamingHandler)get_secret_str)MistralThinkingBlockMistralToolCallMessage)AllMessageValues)ModelResponseModelResponseStream) convert_to_model_response_objectc                       s  e Zd ZU dZdZee ed< dZee ed< dZ	ee ed< dZ
ee ed< dZeed  ed< dZee ed	< dZee ed
< dZee ed< dZeeeef  ed< 									dSdee dee dee dee deed  d	ee d
ee dee deeeef  ddfddZe fddZdedee fddZdedefddZedefddZdededededef
ddZdee dee deeee f fd d!Z e!d"ee" ded#ed$ de#e$e$ee" f fd%d&Z%e!	'dTd"ee" ded#ed' dee" fd(d&Z%	'dTd"ee" ded#edeee" e#e$e$ee" f f f fd)d&Z%d"ee" dedee" f fd*d+Z&d"ee" dedee" f fd,d-Z'd"ee" dee" fd.d/Z(d"ee" dedee" fd0d1Z)ededefd2d3Z*ed4e"de"fd5d6Z+ed4e"de"fd7d8Z,ed4e"defd9d:Z-ed;edefd<d=Z.ed>e/defd?d@Z0ed;edefdAdBZ1ded"ee" dedCedDedef fdEdFZ2		dUdedGe3j4dHe5dIe6dJed"ee" dedCedKe$dee dLee de5fdMdNZ7	'dTdOee8e e9e e5f dPedLee fdQdRZ:  Z;S )VMistralConfiga  
    Reference: https://docs.mistral.ai/api/

    The class `MistralConfig` provides configuration for the Mistral's Chat API interface. Below are the parameters:

    - `temperature` (number or null): Defines the sampling temperature to use, varying between 0 and 2. API Default - 0.7.

    - `top_p` (number or null): An alternative to sampling with temperature, used for nucleus sampling. API Default - 1.

    - `max_tokens` (integer or null): This optional parameter helps to set the maximum number of tokens to generate in the chat completion. API Default - null.

    - `tools` (list or null): A list of available tools for the model. Use this to specify functions for which the model can generate JSON inputs.

    - `tool_choice` (string - 'auto'/'any'/'none' or null): Specifies if/how functions are called. If set to none the model won't call a function and will generate a message instead. If set to auto the model can choose to either generate a message or call a function. If set to any the model is forced to call a function. Default - 'auto'.

    - `stop` (string or array of strings): Stop generation if this token is detected. Or if one of these tokens is detected when providing an array

    - `random_seed` (integer or null): The seed to use for random sampling. If set, different calls will generate deterministic results.

    - `safe_prompt` (boolean): Whether to inject a safety prompt before all conversations. API Default - 'false'.

    - `response_format` (object or null): An object specifying the format that the model must output. Setting to { "type": "json_object" } enables JSON mode, which guarantees the message the model generates is in JSON. When using JSON mode you MUST also instruct the model to produce JSON yourself with a system or a user message.
    Ntemperaturetop_p
max_tokenstools)autoanynonetool_choicerandom_seedsafe_promptresponse_formatstopreturnc
                 C   s>   t   }
|
 D ]\}}|dkr|d urt| j|| q	d S )Nself)localscopyitemssetattr	__class__)r(   r   r   r   r   r"   r#   r$   r%   r&   Zlocals_keyvalue r0   _/home/app/Keep/.python/lib/python3.10/site-packages/litellm/llms/mistral/chat/transformation.py__init__M   s   
zMistralConfig.__init__c                    s
   t   S N)super
get_config)clsr-   r0   r1   r5   ^   s   
zMistralConfig.get_configmodelc                 C   s&   g d}d|  v r|ddg |S )N)streamr   r   r   max_completion_tokensr   r"   seedr&   r%   parallel_tool_calls	magistralthinkingreasoning_effort)lowerextend)r(   r8   Zsupported_paramsr0   r0   r1   get_supported_openai_paramsb   s   z)MistralConfig.get_supported_openai_paramsc                 C   s$   |dks|dkr
|S |dkrdS dS )Nr   r!   requiredr    r0   )r(   r"   r0   r0   r1   _map_tool_choicew   s
   zMistralConfig._map_tool_choicec                   C   s   dS )z
        Returns the system prompt for Mistral reasoning models.
        Based on Mistral's documentation: https://huggingface.co/mistralai/Magistral-Small-2506

        Mistral recommends the following system prompt for reasoning:
        ay  
        <s>[SYSTEM_PROMPT]system_prompt
        A user will ask you to solve a task. You should first draft your thinking process (inner monologue) until you have derived the final answer. Afterwards, write a self-contained summary of your thoughts (i.e. your summary should be succinct but contain all the critical steps you needed to reach the conclusion). You should use Markdown to format your response. Write both your thoughts and summary in the same language as the task posed by the user. NEVER use oxed{} in your response.

        Your thinking process must follow the template below:
        <think>
        Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct answer.
        </think>

        Here, provide a concise summary that reflects your reasoning and presents a clear final answer to the user. Don't mention that this is a summary.

        Problem:

        [/SYSTEM_PROMPT][INST]user_message[/INST]<think>
        reasoning_traces
        </think>
        assistant_response</s>[INST]user_message[/INST]
        r0   r0   r0   r0   r1   $_get_mistral_reasoning_system_prompt   s   z2MistralConfig._get_mistral_reasoning_system_promptnon_default_paramsoptional_paramsdrop_paramsc                 C   s"  |  D ]\}}|dkr||d< |dkr||d< |dkr#| ||d< |dkr/|du r/||d< |dkr7||d< |dkr?||d< |dkrG||d< |d	krXt|trX| j|d
|d	< |dkrbd|i|d< |dkrj||d< |dkrxd| v rxd|d< |dkrd| v rd|d< |dkr||d< q|S )Nr   r:   r   r9   Tr   r   r&   r"   )r"   r;   r#   Z
extra_bodyr%   r?   r=   _add_reasoning_promptr>   r<   )r+   _clean_tool_schema_for_mistral
isinstancestrrD   r@   )r(   rF   rG   r8   rH   paramr/   r0   r0   r1   map_openai_params   s>   
zMistralConfig.map_openai_paramsapi_baseapi_keyc                 C   sF   |pt dpd}|d ur|ds|d }|pt dpt d}||fS )NZMISTRAL_AZURE_API_BASEzhttps://api.mistral.ai/v1z/v1ZMISTRAL_AZURE_API_KEYZMISTRAL_API_KEY)r   endswith)r(   rO   rP   Zdynamic_api_keyr0   r0   r1   $_get_openai_compatible_provider_info   s   z2MistralConfig._get_openai_compatible_provider_infomessagesis_asyncTc                 C      d S r3   r0   r(   rS   r8   rT   r0   r0   r1   _transform_messages   s   z!MistralConfig._transform_messagesFc                 C   rU   r3   r0   rV   r0   r0   r1   rW      s   c                    s   |D ]+}| d}|r-t|tr-tdd |D r-|r#| ||  S | ||}|  S qt|}g }|D ]}t|}t	|}t
|rHq6t|}|| q6|r\t ||dS t ||dS )a  
        - handles scenario where content is list and not string
        - content list is just text, and no images
        - if image passed in, then just return as is (user-intended)
        - if `name` is passed, then drop it for mistral API: https://github.com/BerriAI/litellm/issues/6696

        Motivation: mistral api doesn't support content as a list.
        The above statement is not valid now. Need to plan to remove all the #1,2,3 
        Mistral API supports content as a list.
        contentc                 s   s    | ]
}| d dv V  qdS )type)Z	image_urlfileNget.0cr0   r0   r1   	<genexpr>       z4MistralConfig._transform_messages.<locals>.<genexpr>TF)r\   rK   listr    _transform_messages_async_transform_messages_syncr   r   _handle_name_in_message_handle_tool_call_message_is_empty_assistant_messager   appendr4   rW   )r(   rS   r8   rT   m_content_blockZnew_messagesr7   r0   r1   rW      s*   



c                    s&   t  ||dI dH }| |}|S )zV
        Handle modification of messages for Mistral API in an async context.
        TNr4   rW   _handle_message_with_filer(   rS   r8   r7   r0   r1   rc     s   
z'MistralConfig._transform_messages_asyncc                    s   t  ||d}| |}|S )zL Handle modification of messages for Mistral API in a sync context.
        Frk   rm   r7   r0   r1   rd   #  s   
z&MistralConfig._transform_messages_syncc                 C   sz   |D ]8}| d}|r:t|tr:tdd |D r:dd |D }|D ]}| di  d}|r9||d< |dd q"q|S )	zZ
        Mistral API supports only 'file_id' in message content with type 'file'.
        rX   c                 s       | ]
}| d dkV  qdS )rY   rZ   Nr[   r]   r0   r0   r1   r`   8  ra   z:MistralConfig._handle_message_with_file.<locals>.<genexpr>c                 S   s   g | ]}| d dkr|qS )rY   rZ   r[   r]   r0   r0   r1   
<listcomp>;  s    z;MistralConfig._handle_message_with_file.<locals>.<listcomp>rZ   file_idN)r\   rK   rb   r    pop)r(   rS   ri   rj   file_contentsZfile_contentrp   r0   r0   r1   rl   /  s   
z'MistralConfig._handle_message_with_filec           
      C   s   | dds|S tdd |D }|rct|D ]J\}}| ddkra| dd}|  }t|tr9| d	| }nt|trId
|d	 dg| }n	| d	t| }tti |d|i||<  nqnttd|  d}	|	g| }|	dd |S )zn
        Add reasoning system prompt for Mistral magistral models when reasoning_effort is specified.
        rI   Fc                 s   rn   )rolesystemNr[   )r^   msgr0   r0   r1   r`   N  ra   zGMistralConfig._add_reasoning_system_prompt_if_needed.<locals>.<genexpr>rs   rt   rX    z

text)rY   rw   )rs   rX   N)
r\   r    	enumeraterE   rK   rL   rb   r   r   rq   )
r(   rS   rG   Zhas_system_messageiru   Zexisting_contentZreasoning_promptZnew_contentZreasoning_messager0   r0   r1   &_add_reasoning_system_prompt_if_neededD  s@   


z4MistralConfig._add_reasoning_system_prompt_if_neededc                 C   sB   |s|S ddl }ddlm} ddlm} ||}|||d}|S )a  
        Clean tool schemas to remove fields that cause issues with Mistral API.

        Removes:
        - $id and $schema fields (cause grammar validation errors)
        - additionalProperties=False (causes OpenAI API schema errors)
        - strict field (not supported by Mistral)

        Args:
            tools: List of tool definitions
            max_depth: Maximum recursion depth for schema cleaning (default: 10)

        Returns:
            Cleaned tools list
        r   N)DEFAULT_MAX_RECURSE_DEPTH)_remove_json_schema_refs)	max_depth)r*   Zlitellm.constantsr{   litellm.utilsr|   deepcopy)r6   r   r*   r{   r|   Zcleaned_toolsr0   r0   r1   rJ   y  s   
z,MistralConfig._clean_tool_schema_for_mistralmessagec                 C   sX   | d}|dur*|d dkr|dd |S t|tr*t| dkr*|dd |S )z
        Mistral API only supports `name` in tool messages

        If role == tool, then we keep `name` if it's not an empty string
        Otherwise, we drop `name`
        nameNrs   Ztoolr   )r\   rq   rK   rL   lenstrip)r6   r   _namer0   r0   r1   re     s   
z%MistralConfig._handle_name_in_messagec                 C   sZ   | d}g }|dur+t|tr+|D ]}t| dd| dd}|| q||d< |S )zc
        Mistral API only supports tool_calls in Messages in `MistralToolCallMessage` spec
        Z
tool_callsNidfunction)r   rY   r   )r\   rK   rb   r   rh   )r6   r   Z_tool_callsZmistral_tool_callsZ_toolZ_tool_call_messager0   r0   r1   rf     s   
z'MistralConfig._handle_tool_call_messagec                 C   sb   ddl m} t| }d}|D ]}|dkr.||dur.|dkr)||dkr)qd} |S q|S )	zQ
        Mistral API does not support empty string in assistant content.
        r   )ChatCompletionAssistantMessageTrs   NrX   rv   F)litellm.types.llms.openair   r   keysr\   )r6   r   r   Zset_keysZall_expected_values_are_emptyr.   r0   r0   r1   rg     s    z)MistralConfig._is_empty_assistant_messageresponse_datac                 C   sT   |  dr(t| d dkr(| d D ]}| dr'|d  ddkr'd|d d< q| S )a  
        Handle Mistral-specific behavior where empty string content should be converted to None.

        Mistral API sometimes returns empty string content ('') instead of null,
        which can cause issues with downstream processing.

        Args:
            response_data: The raw response data from Mistral API

        Returns:
            dict: The response data with empty string content converted to None
        choicesr   r   rX   rv   N)r\   r   )r   choicer0   r0   r1   _handle_empty_content_response  s   z,MistralConfig._handle_empty_content_responsethinking_blocksc                 C   s   d dd | d D S )zG
        Convert Mistral thinking blocks to reasoning content.
        
c                 S   s   g | ]}| d dqS )rw   rv   r[   )r^   blockr0   r0   r1   ro     s    zNMistralConfig._convert_thinking_block_to_reasoning_content.<locals>.<listcomp>r>   )join)r   r0   r0   r1   ,_convert_thinking_block_to_reasoning_content  s   z:MistralConfig._convert_thinking_block_to_reasoning_contentc           	   	   C   s   |  dryt| d dkry| d D ]g}| drx|d  drx|d d }t|trxd}d}|D ]9}| ddkr\| dg }g }|D ]}| ddkrU|| dd qCd	|}q0| ddkri| dd}q0||d d< |rx||d d
< q| S )z
        Handle Mistral's content list format and extract thinking content.

        Map mistral's content list to string and extract thinking blocks:
        - Thinking block -> reasoning_content field
        - Text block -> content field
        r   r   r   rX   rv   rY   r>   rw   r   reasoning_content)r\   r   rK   rb   rh   r   )	r   r   rX   Zthinking_contentZtext_contentr   r   Zthinking_textsthinking_blockr0   r0   r1   &_handle_content_list_to_str_conversion  s4   


z4MistralConfig._handle_content_list_to_str_conversionlitellm_paramsheadersc                    s:   d|  v r|ddr| ||}t j|||||dS )a  
        Transform the overall request to be sent to the API.
        For magistral models, adds reasoning system prompt when reasoning_effort is specified.

        Returns:
            dict: The transformed request. Sent as the body of the API call.
        r=   rI   F)r8   rS   rG   r   r   )r@   r\   rz   r4   transform_request)r(   r8   rS   rG   r   r   r7   r0   r1   r     s   zMistralConfig.transform_requestraw_responsemodel_responselogging_objrequest_dataencoding	json_modec                 C   s\   |j |jd |j|jd< | }| |}| |}ttt	||d|jit
|jd}|S )z
        Transform the raw response from Mistral API.
        Handles Mistral-specific behavior like converting empty string content to None
        and extracting thinking content from content lists.
        )original_responseresponse_headersr   )Zresponse_objectZmodel_response_objectZhidden_paramsZ_response_headers)Z	post_callrw   r   Zmodel_call_detailsjsonr   r   r   r   r   dict)r(   r8   r   r   r   r   rS   rG   r   r   rP   r   r   Zfinal_response_objr0   r0   r1   transform_response<  s   


z MistralConfig.transform_responsestreaming_responsesync_streamc                 C   s   t |||dS )N)r   r   r   )MistralChatResponseIterator)r(   r   r   r   r0   r0   r1   get_model_response_iteratorc  s
   z)MistralConfig.get_model_response_iterator)	NNNNNNNNN)F)NN)<__name__
__module____qualname____doc__r   r   int__annotations__r   r   r   rb   r"   r   r#   r$   boolr%   r   r&   r
   rL   r2   classmethodr5   r   rB   rD   staticmethodrE   rN   r	   rR   r   r   r   r   rW   rc   rd   rl   rz   rJ   re   rf   rg   r   r   r   r   r   httpxResponser   LiteLLMLoggingObjr   r   r   r   __classcell__r0   r0   r7   r1   r   *   sx  
 
	


+

*

5!
(+	

+r   c                	       sV   e Zd Zdedef fddZedee dee	e
 ee e	e
 f fddZ  ZS )r   chunkr'   c                    s   z>| dg D ]5}| di }| d}t|tr<| |\}}}||d< |r0||d< ||d< q|dd  |dd  qW n tyM   t | Y S w t |S )Nr   deltarX   r   r   )r\   rK   rb   _normalize_content_blocksrq   	Exceptionr4   chunk_parser)r(   r   r   r   rX   normalized_textr   r   r7   r0   r1   r   q  s,   


z(MistralChatResponseIterator.chunk_parsercontent_blocksc                 C   s   g }g }g }| D ]K}| d}|dkrF| dg }g }|D ]}| ddkr/|| dd qd|}	|	rE||	 |d|	dd q|dkrS|| dd q|r[d|nd}
|rdd|nd}|
||fS )	zl
        Convert Mistral magistral content blocks into OpenAI-compatible content + thinking_blocks.
        rY   r>   rw   rv   Zmistral)rY   r>   	signatureNr   )r\   rh   r   )r   Ztext_segmentsr   Zreasoning_segmentsr   Z
block_typeZmistral_thinkingZthinking_text_partsr   Zthinking_textr   r   r0   r0   r1   r     s8   



z5MistralChatResponseIterator._normalize_content_blocks)r   r   r   r   r   r   r   r   r	   r   rL   r   r   r0   r0   r7   r1   r   p  s    r   )&r   typingr   r   r   r   r   r   r   r	   r
   r   r   r   r   Z*litellm.litellm_core_utils.litellm_loggingr   r   Z8litellm.litellm_core_utils.prompt_templates.common_utilsr   r   Z+litellm.llms.openai.chat.gpt_transformationr   r   Zlitellm.secret_managers.mainr   Zlitellm.types.llms.mistralr   r   r   r   Zlitellm.types.utilsr   r   r~   r   r   r   r0   r0   r0   r1   <module>   s"    8    J