
    qil8                        U d Z ddlZddlZddlZddlZddlmZ ddlmZm	Z	m
Z
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mZ dd	lmZ dd
lmZmZ ddlmZ erddlm Z  eZ!eZ"eZeZe G d d             Z#ee!e#f   Z$ee%d<    G d d      Z&y)a|  Universal LLM context management for LLM services in Pipecat.

Context contents are represented in a universal format (based on OpenAI)
that supports a union of known Pipecat LLM service functionality.

Whenever an LLM service needs to access context, it does a just-in-time
translation from this universal context into whatever format it needs, using a
service-specific adapter.
    N)	dataclass)TYPE_CHECKINGAnyListOptional	TypeAliasUnion)logger)	NOT_GIVEN)NotGiven)ChatCompletionMessageParam#ChatCompletionToolChoiceOptionParam)Image)AdapterTypeToolsSchema)AudioRawFrame)OpenAILLMContextc                   &    e Zd ZU dZeed<   eed<   y)LLMSpecificMessagezA container for a context message that is specific to a particular LLM service.

    Enables the use of service-specific message types while maintaining
    compatibility with the universal LLM context format.
    llmmessageN)__name__
__module____qualname____doc__str__annotations__r        \/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/processors/aggregators/llm_context.pyr   r   2   s     
HLr   r   LLMContextMessagec                   J   e Zd ZdZed'd       Zdeefdeee	      de
ez  deez  fdZed	dd
dededee   de	fd       Zed	dd
dededeeef   dedee   de	fd       Zed	dd
dedee   dede	fd       Zedee	   fd       Zdee	   fdZd(dee   dee	   fdZede
ez  fd       Zedeez  fd       Zde	fdZdee	   fdZdee	   fdZefde
ez  fd Z deez  fd!Z!dd	d"dedeeef   dedee   def
d#Z"dd$dee   defd%Z#ede
ez  de
ez  fd&       Z$y))
LLMContextzManages conversation context for LLM interactions.

    Handles message history, tool definitions, tool choices, and multimedia
    content for LLM conversations. Provides methods for message manipulation,
    and content formatting.
    returnc                 d   ddl }|j                         5  |j                  d       |j                  dt        d       ddd       | j
                  }t        |t              rt        g t        j                  |i      }t        | j                         || j                        S # 1 sw Y   hxY w)	a  Create a universal LLM context from an OpenAI-specific context.

        NOTE: this should only be used internally, for facilitating migration
        from OpenAILLMContext to LLMContext. New user code should use
        LLMContext directly.

        .. deprecated:: 0.0.99
            `from_openai_context()` is deprecated and will be removed in a future version.
            Directly use the universal `LLMContext` and `LLMContextAggregatorPair` instead.
            See `OpenAILLMContext` docstring for migration guide.

        Args:
            openai_context: The OpenAI LLM context to convert.

        Returns:
            New LLMContext instance with converted messages and settings.
        r   Nalwayszfrom_openai_context() (likely invoked by create_context_aggregator()) is deprecated and will be removed in a future version. Directly use the universal LLMContext and LLMContextAggregatorPair instead. See OpenAILLMContext docstring for migration guide.   
stacklevel)standard_toolscustom_tools)messagestoolstool_choice)warningscatch_warningssimplefilterwarnDeprecationWarningr-   
isinstancelistr   r   SHIMr#   get_messagesr.   )openai_contextr/   converted_toolss      r    from_openai_contextzLLMContext.from_openai_contextI   s    & 	$$& 	!!(+MMF #  	 )..ot,)!1A1A?0SO #002!&22
 	
%	 	s   *B&&B/Nr,   r-   r.   c                 \    |r|ng | _         t        j                  |      | _        || _        y)zInitialize the LLM context.

        Args:
            messages: Initial list of conversation messages.
            tools: Available tools for the LLM to use.
            tool_choice: Tool selection strategy for the LLM.
        N)	_messagesr#   _normalize_and_validate_tools_tools_tool_choice)selfr,   r-   r.   s       r    __init__zLLMContext.__init__v   s+     ?G(B.8.V.VW\.]=Hr   user)roletextrC   urlrD   c                 h    g }|r|j                  d|d       |j                  dd|id       | |dS )zCreate a context message containing an image URL.

        Args:
            role: The role of this message (defaults to "user").
            url: The URL of the image.
            text: Optional text to include with the image.
        rD   typerD   	image_urlrE   )rH   rI   rC   content)append)rC   rE   rD   rK   s       r    create_image_url_messagez#LLMContext.create_image_url_message   s=     NNFD9:5#,GH11r   formatsizeimagec                    K   j                  d      fd}t        j                  |       d{   }drnd d| }t        j	                  | ||      S 7 (w)a  Create a context message containing an image.

        Args:
            role: The role of this message (defaults to "user").
            format: Image format (e.g., 'RGB', 'RGBA', or, if already encoded,
                the MIME type like 'image/jpeg').
            size: Image dimensions as (width, height) tuple.
            image: Raw image bytes.
            text: Optional text to include with the image.
        zimage/c                      r} nLt        j                         }t        j                        j	                  |d       |j                         } t        j                  |       j                  d      }|S )NJPEG)rN   utf-8)	ioBytesIOr   	frombytessavegetvaluebase64	b64encodedecode)bytesbufferencoded_imagerN   rP   image_already_encodedrO   s      r    encode_imagez5LLMContext.create_image_message.<locals>.encode_image   se    $ e499&9P)",,U3::7CM  r   Nzdata:z
image/jpegz;base64,)rC   rE   rD   )
startswithasyncio	to_threadr#   rM   )	rC   rN   rO   rP   rD   ra   r_   rE   r`   s	    ```    @r    create_image_messagezLLMContext.create_image_message   sk     ( !' 1 1( ;		! &//== 5f<HQ^P_`22#D2QQ	 >s   2A#A!)A#zAudio followsaudio_framesc                    K   d|dg}fd}t        j                  |       d{   }|j                  d|ddd       | |d	S 7  w)
a   Create a context message containing audio.

        Args:
            role: The role of this message (defaults to "user").
            audio_frames: List of audio frame objects to include.
            text: Optional text to include with the audio.
        rD   rG   c                  
   d   j                   } d   j                  }dj                  d D              }t        j                         5 }t        j                  |d      5 }|j                  d       |j                  |       |j                  |        |j                  |       d d d        t        j                  |j                               j                  d      }d d d        |S # 1 sw Y   ExY w# 1 sw Y   S xY w)Nr   r   c              3   4   K   | ]  }|j                     y wN)audio).0frames     r    	<genexpr>zHLLMContext.create_audio_message.<locals>.encode_audio.<locals>.<genexpr>   s     BEEKKBs   wbr'   rT   )sample_ratenum_channelsjoinrU   rV   waveopensetsampwidthsetnchannelssetframeratewriteframesrZ   r[   rY   r\   )rp   rq   datar^   wfencoded_audiorf   s         r    encode_audioz5LLMContext.create_audio_message.<locals>.encode_audio   s    &q/55K'?77L88B\BBD TYYvt, )OOA&OOL1OOK0NN4(	) !' 0 01B C J J7 ST ! ) )T ! s%   C8#AC,(:C8,C5	1C88DNinput_audiowav)ry   rN   )rH   r}   rJ   )rc   rd   rL   )rC   rf   rD   rK   r|   r{   s    `    r    create_audio_messagezLLMContext.create_audio_message   s^      #D12	!  &//==%(5G	
 11 >s   $A
A!A
c                 "    | j                         S )a1  Get the current messages list.

        NOTE: This is equivalent to calling `get_messages()` with no filter. If
        you want to filter out LLM-specific messages that don't pertain to your
        LLM, use `get_messages()` directly.

        Returns:
            List of conversation messages.
        )r7   r@   s    r    r,   zLLMContext.messages   s       ""r   c                     ddl }|j                         5  |j                  d       |j                  dt        d       ddd       | j                         S # 1 sw Y   | j                         S xY w)a  Get messages suitable for persistent storage.

        NOTE: the only reason this method exists is because we're "silently"
        switching from OpenAILLMContext to LLMContext under the hood in some
        services and don't want to trip up users who may have been relying on
        this method, which is part of the public API of OpenAILLMContext but
        doesn't need to be for LLMContext.

        .. deprecated:: 0.0.92
            Use `get_messages()` instead.

        Returns:
            List of conversation messages.
        r   Nr&   zPget_messages_for_persistent_storage() is deprecated, use get_messages() instead.r'   r(   )r/   r0   r1   r2   r3   r7   )r@   r/   s     r    #get_messages_for_persistent_storagez.LLMContext.get_messages_for_persistent_storage   sm     	$$& 	!!(+MMb"  	   ""	   ""s   *AA/llm_specific_filterc                    || j                   S | j                   D cg c]#  }t        |t              r|j                  |k(  r|% }}t	        |      t	        | j                         k  rt        j                  d| d       |S c c}w )a  Get the current messages list.

        Args:
            llm_specific_filter: Optional filter to return LLM-specific
                messages for the given LLM, in addition to the standard
                messages. If messages end up being filtered, an error will be
                logged; this is intended to catch accidental use of
                incompatible LLM-specific messages.

        Returns:
            List of conversation messages.
        z<Attempted to use incompatible LLMSpecificMessages with LLM 'z'.)r<   r4   r   r   lenr
   error)r@   r   msgfiltered_messagess       r    r7   zLLMContext.get_messages  s     &>>! ~~
c#56#''EX:X 
 

  !C$77LLNObNccef ! 
s   (Bc                     | j                   S )zFGet the tools list.

        Returns:
            Tools list.
        )r>   r   s    r    r-   zLLMContext.tools.  s     {{r   c                     | j                   S )zjGet the current tool choice setting.

        Returns:
            The tool choice configuration.
        r?   r   s    r    r.   zLLMContext.tool_choice7  s        r   r   c                 :    | j                   j                  |       y)zAdd a single message to the context.

        Args:
            message: The message to add to the conversation history.
        N)r<   rL   )r@   r   s     r    add_messagezLLMContext.add_message@  s     	g&r   c                 :    | j                   j                  |       y)zAdd multiple messages to the context.

        Args:
            messages: List of messages to add to the conversation history.
        N)r<   extendr@   r,   s     r    add_messageszLLMContext.add_messagesH  s     	h'r   c                 "    || j                   dd y)zReplace all messages in the context.

        Args:
            messages: New list of messages to replace the current history.
        N)r<   r   s     r    set_messageszLLMContext.set_messagesP  s     %qr   c                 8    t         j                  |      | _        y)z|Set the available tools for the LLM.

        Args:
            tools: A ToolsSchema or NOT_GIVEN to disable tools.
        N)r#   r=   r>   )r@   r-   s     r    	set_toolszLLMContext.set_toolsX  s     !>>uEr   c                     || _         y)zxSet the tool choice configuration.

        Args:
            tool_choice: Tool selection strategy for the LLM.
        Nr   )r@   r.   s     r    set_tool_choicezLLMContext.set_tool_choice`  s     (r   )rD   rC   c                v   K   t         j                  |||||       d{   }| j                  |       y7 w)a  Add a message containing an image frame.

        Args:
            format: Image format (e.g., 'RGB', 'RGBA', or, if already encoded,
                the MIME type like 'image/jpeg').
            size: Image dimensions as (width, height) tuple.
            image: Raw image bytes.
            text: Optional text to include with the image.
            role: The role of this message (defaults to "user").
        )rC   rN   rO   rP   rD   N)r#   re   r   )r@   rN   rO   rP   rD   rC   r   s          r    add_image_frame_messagez"LLMContext.add_image_frame_messageh  sB     & #77f4u4 8 
 
 	!
s   979)rD   c                p   K   t         j                  ||       d{   }| j                  |       y7 w)zAdd a message containing audio frames.

        Args:
            audio_frames: List of audio frame objects to include.
            text: Optional text to include with the audio.
        )rf   rD   N)r#   r   r   )r@   rf   rD   r   s       r    add_audio_frames_messagez#LLMContext.add_audio_frames_message  s5      #77\X\7]]! ^s   646c                     t        | t              r | j                  s| j                  st        S | S | t        u rt        S t        dt        |              )zNormalize and validate the given tools.

        Raises:
            TypeError: If tools are not a ToolsSchema or NotGiven.
        zJIn LLMContext, tools must be a ToolsSchema object or NOT_GIVEN. Got type: )r4   r   r*   r+   r   	TypeErrorrH   )r-   s    r    r=   z(LLMContext._normalize_and_validate_tools  sW     e[)''0B0B  Li\]abg]h\ij r   )r8   r   r$   r#   rj   )%r   r   r   r   staticmethodr:   r   r   r   r!   r   r   LLMContextToolChoicerA   r   rM   tupleintr]   re   r5   r   r   propertyr,   r   r7   r-   r.   r   r   r   r   r   r   r   r=   r   r   r    r#   r#   A   s    *
 *
\ 7;(17@	I4 123I X%I *H4	I"  "	22 2 sm	2
 
2 2*   #$R$R $R CHo	$R
 $R sm$R 
$R $RL o%2%2-1--@%2HK%2	%2 %2N 
#$01 
# 
##T:K5L #6! !N_I` !4 {X-   !1H< ! !'#4 '(T*;%< (%T*;%< % 9B F{X5 F(+?(+J ( #" " CHo	"
 " sm" "2 AP
"#M2
":=
" [8-C V^H^  r   r#   )'r   rc   rZ   rU   rs   dataclassesr   typingr   r   r   r   r   r	   logurur
   openai._typesr   OPEN_AI_NOT_GIVENr   OpenAINotGivenopenai.types.chatr   r   PILr   %pipecat.adapters.schemas.tools_schemar   r   pipecat.frames.framesr   1pipecat.processors.aggregators.openai_llm_contextr   LLMStandardMessager   r   r!   r   r#   r   r   r    <module>r      s      	  ! G G  8 4  J /R 0 : 	     %%79K%KL 9 L[ [r   