
    qi&                         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 dd
lmZ e G d de             Z G d de      Zy)zEMistral LLM service implementation using OpenAI-compatible interface.    )	dataclass)ListOptionalSequence)logger)ChatCompletionMessageParam)OpenAILLMInvocationParams)FunctionCallFromLLM)OpenAILLMSettings)OpenAILLMService)_warn_deprecated_paramc                       e Zd ZdZy)MistralLLMSettingszSettings for MistralLLMService.N)__name__
__module____qualname____doc__     N/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/services/mistral/llm.pyr   r      s    )r   r   c            
            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
Z	d fd	Z
dee   dee   fdZdee   f fdZdedefdZ x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                    t        d      }|t        dt         d       ||_        ||j                  |       t	        |   d|||d| y)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=OpenAILLMSettings(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   )r   r   r   apply_updatesuper__init__)selfr   r   r   r   kwargsdefault_settings	__class__s          r   r!   zMistralLLMService.__init__'   s`    0 .4JK "7,>H%*"
 ))(3a8FVaZ`ar   c                 V    t        j                  d|        t        |   ||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%   s       r   r(   zMistralLLMService.create_clientN   s0     	8
CDw$WhA&AAr   messagesreturnc                 Z   |s|S |D cg c]  }t        |       }}g }t        |      D ]Z  \  }}|j                  d      dk(  s|t        |      dz
  k(  s||dz      j                  d      dk7  sG|j	                  |dz          \ t        |      D ]  }|j                  |ddd        t        d t        |      D        t        |            }t        |      D ]%  \  }}|j                  d      dk(  s||k\  s!d	|d<   ' |d
   }|j                  d      dk(  r	d|vrd|d<   |S c c}w )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.
        roletool   	assistant r,   contentc              3   P   K   | ]  \  }}|j                  d       dk7  s|   yw)r,   systemN)get).0imsgs      r   	<genexpr>z:MistralLLMService._apply_mistral_fixups.<locals>.<genexpr>   s#     V61c#''&/X:UQVs   &&r4   userprefixT)dict	enumerater5   lenappendreversedinsertnext)	r"   r)   r8   fixed_messagesassistant_insert_indicesr7   idxfirst_non_system_idxlast_messages	            r   _apply_mistral_fixupsz'MistralLLMService._apply_mistral_fixups\   s]   & O 088$s)88 $& / 	;FAswwv&(N+a//>!a%3H3L3LV3TXc3c,33AE:		;
 45 	NC!!#'LM	N  $VY~6V 
  / 	%FAswwv(*q4H/H$F	%
 &b) F#{2x|7S%)L"; 9s   D(function_callsc                   K   |syg }|r|d   j                   j                         ng }t               }|D ]I  }|j                  d      dk(  s|j                  d      s*|j	                  |j                  d             K |D ]Q  }|j
                  |vr|j                  |       #t        j                  d|j                   d|j
                          S |rt        | -  |       d{    yy7 w)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setr5   addrL   r@   r   tracefunction_namer    run_function_calls)r"   rJ   calls_to_executer)   executed_call_idsr8   callr%   s          r   rT   z$MistralLLMService.run_function_calls   s     >   @N>!$,,99;SU  E 	?Cwwv&(SWW^-D!%%cggn&=>	?
 # 	D  (99 ''-?@R@R?SSTUYUfUfTgh		 ',-=>>> >s   A
C5C5 BC5,C3-C5params_from_contextc                 |   | j                  |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                  j                  r2|j                  dg       }d| j                  j                  dg|z   |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)   rZ   r[   frequency_penaltypresence_penaltytemperaturetop_p
max_tokensrandom_seedr4   r1   )rI   r   r   r]   r^   r_   r`   ra   seedupdateextrasystem_instructionr5   )r"   rX   rD   paramsr)   s        r   build_chat_completion_paramsz.MistralLLMService.build_chat_completion_params   s    334G
4ST ^^))&(1.}=!%!A!A $ ? ?>>55^^))..33
 >>$(NN$7$7F=! 	dnn**+ >>,,zz*b1H!dnn.O.OP""F: r   )NN)r   r   r   r   r   Settings__annotations__strr   r!   r(   r   r   rI   r   r
   rT   r	   r=   rh   __classcell__)r%   s   @r   r   r      s     "H!! 4#15%b %b 	%b
 }%b -.%bNB4784	(	)4l9?x@S7T 9?v'@Y '^b 'r   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   pipecat.services.settingsr   r   r   r   r   r   <module>rv      sT    L ! + +  8 O 5 > 8 < 	* 	 	W( Wr   