o
    i&                     @   s   d Z ddlmZ ddlmZmZmZ ddlmZ ddl	m
Z
 ddlmZ ddlmZ ddlmZ dd	lmZ eG d
d dejZG dd deZdS )zEMistral LLM service implementation using OpenAI-compatible interface.    )	dataclass)ListOptionalSequence)logger)ChatCompletionMessageParam)OpenAILLMInvocationParams)FunctionCallFromLLM)BaseOpenAILLMService)OpenAILLMServicec                   @   s   e Zd ZdZdS )MistralLLMSettingszSettings for MistralLLMService.N)__name__
__module____qualname____doc__ r   r   P/home/ubuntu/.local/lib/python3.10/site-packages/pipecat/services/mistral/llm.pyr      s    r   c                
       s   e Zd ZU dZeZeed< dddddededee d	ee f fd
dZ	d fdd	Z
dee dee fddZdee f fddZdedefddZ  ZS )MistralLLMServicezA service for interacting with Mistral's API using the OpenAI-compatible interface.

    This service extends OpenAILLMService to connect to Mistral's API endpoint while
    maintaining full compatibility with OpenAI's interface and functionality.
    	_settingszhttps://api.mistral.ai/v1N)base_urlmodelsettingsapi_keyr   r   r   c                   sV   | j dd}|dur| dd ||_|dur|| t jd|||d| dS )a  Initialize the Mistral LLM service.

        Args:
            api_key: The API key for accessing Mistral's API.
            base_url: The base URL for Mistral API. Defaults to "https://api.mistral.ai/v1".
            model: The model identifier to use. Defaults to "mistral-small-latest".

                .. deprecated:: 0.0.105
                    Use ``settings=MistralLLMService.Settings(model=...)`` instead.

            settings: Runtime-updatable settings. When provided alongside deprecated
                parameters, ``settings`` values take precedence.
            **kwargs: Additional keyword arguments passed to OpenAILLMService.
        zmistral-small-latest)r   Nr   )r   r   r   r   )Settings"_warn_init_param_moved_to_settingsr   apply_updatesuper__init__)selfr   r   r   r   kwargsdefault_settings	__class__r   r   r   &   s   
zMistralLLMService.__init__c                    s&   t d|  t j||fi |S )a  Create OpenAI-compatible client for Mistral API endpoint.

        Args:
            api_key: The API key for authentication. If None, uses instance key.
            base_url: The base URL for the API. If None, uses instance URL.
            **kwargs: Additional arguments passed to the client constructor.

        Returns:
            An OpenAI-compatible client configured for Mistral API.
        z!Creating Mistral client with api )r   debugr   create_client)r   r   r   r   r!   r   r   r$   M   s   zMistralLLMService.create_clientmessagesreturnc           	      C   s  |s|S dd |D }g }t |D ]%\}}|ddkr6|t|d ks/||d  ddkr6||d  qt|D ]}||ddd q;td	d
 t |D t|}t |D ]\}}|ddkrl||krld|d< qY|d }|ddkrd|vrd|d< |S )a-  Apply fixups to messages to meet Mistral-specific requirements.

        1. A "tool"-role message must be followed by an assistant message.

        2. "system"-role messages must only appear at the start of a
           conversation.

        3. Assistant messages must have prefix=True when they are the final
           message in a conversation (but at no other point).

        Args:
            messages: The original list of messages.

        Returns:
            Messages with Mistral prefix requirement applied to final assistant message.
        c                 S   s   g | ]}t |qS r   )dict).0msgr   r   r   
<listcomp>r   s    z;MistralLLMService._apply_mistral_fixups.<locals>.<listcomp>roletool   	assistant r+   contentc                 s   s&    | ]\}}| d dkr|V  qdS )r+   systemN)get)r(   ir)   r   r   r   	<genexpr>   s   $ z:MistralLLMService._apply_mistral_fixups.<locals>.<genexpr>r2   userprefixT)	enumerater3   lenappendreversedinsertnext)	r   r%   fixed_messagesassistant_insert_indicesr4   r)   idxfirst_non_system_idxlast_messager   r   r   _apply_mistral_fixups[   s.   &z'MistralLLMService._apply_mistral_fixupsfunction_callsc                    s   |sdS g }|r|d j  ng }t }|D ]}|ddkr-|dr-||d q|D ]}|j|vr=|| q0td|j	 d|j  q0|rXt
 |I dH  dS dS )a  Execute function calls, filtering out already-completed ones.

        Mistral and OpenAI have different function call detection patterns:

        OpenAI (Stream-based detection):

        - Detects function calls only from streaming chunks as the LLM generates them
        - Second LLM completion doesn't re-detect existing tool_calls in message history
        - Function calls execute exactly once

        Mistral (Message-based detection):

        - Detects function calls from the complete message history on each completion
        - Second LLM completion with the response re-detects the same tool_calls from
          previous messages
        - Without filtering, function calls would execute twice

        This method prevents duplicate execution by:

        1. Checking message history for existing tool result messages
        2. Filtering out function calls that already have corresponding results
        3. Only executing function calls that haven't been completed yet

        Note: This filtering prevents duplicate function execution, but the
        on_function_calls_started event may still fire twice due to the detection
        pattern difference. This is expected behavior.

        Args:
            function_calls: The function calls to potentially execute.
        Nr   r+   r,   tool_call_idz)Skipping already-executed function call: :)contextget_messagessetr3   addrF   r;   r   tracefunction_namer   run_function_calls)r   rE   calls_to_executer%   executed_call_idsr)   callr!   r   r   rN      s&   
z$MistralLLMService.run_function_callsparams_from_contextc                 C   s   |  |d }| jjd||d |d | jj| jj| jj| jj| jjd
}| jjr.| jj|d< |	| jj
 | jjr^|dg }|rR|d dd	krRt|  d
 d	| jjdg| |d< |S )a  Build parameters for Mistral chat completion request.

        Handles Mistral-specific requirements including:
        - Assistant message prefix requirement for API compatibility
        - Parameter mapping (random_seed instead of seed)
        - Core completion settings
        r%   Ttoolstool_choice)
r   streamr%   rS   rT   frequency_penaltypresence_penaltytemperaturetop_p
max_tokensrandom_seedr   r+   r2   zc: Both system_instruction and an initial system message in context are set. This may be unintended.r0   )rD   r   r   rV   rW   rX   rY   rZ   seedupdateextrasystem_instructionr3   r   warning)r   rR   r?   paramsr%   r   r   r   build_chat_completion_params   s4   	z.MistralLLMService.build_chat_completion_params)NN)r   r   r   r   r   r   __annotations__strr   r   r$   r   r   rD   r   r	   rN   r   r'   rb   __classcell__r   r   r!   r   r      s0   
 '
6;r   N)r   dataclassesr   typingr   r   r   logurur   openai.types.chatr   )pipecat.adapters.services.open_ai_adapterr   pipecat.frames.framesr	    pipecat.services.openai.base_llmr
   pipecat.services.openai.llmr   r   r   r   r   r   r   r   <module>   s   