
    qil                     b   d Z ddlZddlZddlmZmZ ddl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mZmZmZ 	 dd
lmZmZmZmZmZm Z   G d de      Z% G d dee%         Z&y# e!$ r7Z" ejF                  de"         ejF                  d        e$de"       dZ"["ww xY w)zGemini LLM adapter for Pipecat.    N)	dataclassfield)AnyDictListOptional	TypedDict)logger)NotGiven)BaseLLMAdapter)AdapterTypeToolsSchema)
LLMContextLLMContextMessageLLMSpecificMessageLLMStandardMessage)BlobContentFileDataFunctionCallFunctionResponsePartzException: zHIn order to use Google AI, you need to `pip install pipecat-ai[google]`.zMissing module: c                   H    e Zd ZU dZee   ed<   ee   ed<   ee	   e
z  ed<   y)GeminiLLMInvocationParamsz1Context-based parameters for invoking Gemini LLM.system_instructionmessagestoolsN)__name__
__module____qualname____doc__r   str__annotations__r   r   r   r        Z/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/adapters/services/gemini_adapter.pyr   r   "   s(    ; %7m9xr%   r   c                      e Zd ZdZedefd       ZdedefdZ	de
deeeef      fdZdedeeeef      fdZe G d	 d
             Ze G d d             Ze G d d             Zdee   defdZdededefdZdee   dee   dee   fdZdee   dee   ddfdZdededefdZdededefdZdededefdZ d e!dedefd!Z"y)"GeminiLLMAdaptera2  Gemini-specific adapter for Pipecat.

    Handles:
    - Extracting parameters for Gemini's API from a universal LLM context
    - Converting Pipecat's standardized tools schema to Gemini's function-calling format.
    - Extracting and sanitizing messages from the LLM context for logging with Gemini.
    returnc                      y)zCGet the identifier used in LLMSpecificMessage instances for Google.googler$   )selfs    r&   id_for_llm_specific_messagesz-GeminiLLMAdapter.id_for_llm_specific_messages3   s     r%   contextc                     | j                  | j                  |            }|j                  |j                  | j	                  |j
                        dS )zGet Gemini-specific LLM invocation parameters from a universal LLM context.

        Args:
            context: The LLM context containing messages, tools, etc.

        Returns:
            Dictionary of parameters for Gemini's API.
        )r   r   r   ) _from_universal_context_messagesget_messagesr   r   from_standard_toolsr   )r,   r.   r   s      r&   get_llm_invocation_paramsz*GeminiLLMAdapter.get_llm_invocation_params8   sM     889J9J79ST"*"="= ))--gmm<	
 	
r%   tools_schemac                 ~   dt         t        t        f   dt         t        t        f   ffd|j                  }|rDg }|D ]7  }|j	                         } |d   d         |d   d<   |j                  |       9 d|ig}ng }g }|j                  r*|j                  j                  t        j                  g       }||z   S )aj  Convert tool schemas to Gemini's function-calling format.

        Args:
            tools_schema: The tools schema containing standard and custom tool definitions.

        Returns:
            List of tool definitions formatted for Gemini's function-calling API.
            Includes both converted standard tools and any custom Gemini-specific tools.
        schemar)   c                 0   t        | t              s| S i }| j                         D ]h  \  }}|dk(  rt        |t              r |      ||<   (t        |t              r,|D cg c]  }t        |t              r |      n| c}||<   d|||<   j |S c c}w )a   Recursively remove "additionalProperties" fields from JSON schema, as they're not supported by Gemini.

            Args:
                schema: The JSON schema dict to process.

            Returns:
                JSON schema dict with "additionalProperties" stripped out.
            additionalProperties)
isinstancedictitemslist)r6   resultkeyvalueitem_strip_additional_propertiess        r&   rA   zOGeminiLLMAdapter.to_provider_tools_format.<locals>._strip_additional_propertiesT   s     fd+F$lln (
U00t,">u"EF3Kt, %*#  ?It>T4T:Z^^#F3K
 #(F3K( M#s   "!B
parameters
propertiesfunction_declarations)
r   r"   r   standard_toolsto_default_dictappendcustom_toolsgetr   GEMINI)	r,   r4   functions_schemaformatted_functionsfunc	func_dictformatted_standard_toolscustom_gemini_toolsrA   s	           @r&   to_provider_tools_formatz)GeminiLLMAdapter.to_provider_tools_formatI   s    	c3h 	DcN 	: (66"$( 6 002	8Tl+L99	,'5 $**956 *ABU(V'W$')$ $$".";";"?"?@R@RTV"W'*===r%   c                 T   | j                  | j                  |            j                  }g }|D ]H  }|j                         }	 d|v r |d   D ]  }d|v rd|d   d<   d|v sd|d<    |j                  |       J |S # t        $ r"}t        j                  d|        Y d}~;d}~ww xY w)aT  Get messages from a universal LLM context in a format ready for logging about Gemini.

        Removes or truncates sensitive data like image content for safe logging.

        Args:
            context: The LLM context containing messages.

        Returns:
            List of messages in a format ready for logging about Gemini.
        partsinline_data...datathought_signaturezError: N)r0   r1   r   to_json_dict	Exceptionr
   debugrG   )r,   r.   r   messages_for_loggingmessageobjpartes           r&   get_messages_for_loggingz)GeminiLLMAdapter.get_messages_for_logging   s     889J9J79ST]]  " 	-G&&(C,c> #G >(D0:?D/7.$68=D!45	> !'',	- $#  ,wqc]++,s   A< A<<	B'B""B'c                   6    e Zd ZU dZee   ed<   dZee	   ed<   y)"GeminiLLMAdapter.ConvertedMessageszIContainer for Google-formatted messages converted from universal context.r   Nr   )
r   r   r    r!   r   r   r#   r   r   r"   r$   r%   r&   ConvertedMessagesrb      s    Ww-,0HSM0r%   rc   c                   `    e Zd ZU dZdZee   ed<   dZee	   ed<    e
e      Zee	e	f   ed<   y)(GeminiLLMAdapter.MessageConversionResulta/  Result of converting a single universal context message to Google format.

        Either content (a Google Content object) or a system instruction string
        is guaranteed to be set.

        Also returns a tool call ID to name mapping for any tool calls
        discovered in the message.
        Ncontentr   )default_factorytool_call_id_to_name_mapping)r   r   r    r!   rf   r   r   r#   r   r"   r   r:   rh   r   r$   r%   r&   MessageConversionResultre      s?    	 &*'"),0HSM07<T7R$d38nRr%   ri   c                   0    e Zd ZU dZeed<   eeef   ed<   y)(GeminiLLMAdapter.MessageConversionParamszNParameters for converting a single universal context message to Google format.already_have_system_instructionrh   N)r   r   r    r!   boolr#   r   r"   r$   r%   r&   MessageConversionParamsrk      s    \)--&*38n4r%   rn   universal_context_messagesc           	      x   d}g }i }g }|D ]  }t        |t              rpt        |j                  t              r:|j                  j	                  d      dk(  r|j                  |j                         h|j                  |j                         | j                  || j                  t        |      |            }|j                  r|j                  |j                         n|j                  r|j                  }|j                  s|j                  |j                          | j                  ||       | j                  ||      }t        d |D              }|r)|s'|j                  t!        dt#        |      g	             |D 	cg c]  }	|	j$                  s|	 }}	| j'                  ||
      S c c}	w )a  Restructures messages to ensure proper Google format and message ordering.

        This method handles conversion of OpenAI-formatted messages to Google format,
        with special handling for function calls, function responses, and system messages.
        System messages are added back to the context as user messages when needed.

        The final message order is preserved as:

        1. Function calls (from model)
        2. Function responses (from user)
        3. Text messages (converted from system messages)

        Note::

            System messages are only added back when there are no regular text
            messages in the context, ensuring proper conversation continuity
            after function calls.
        NtyperW   )rl   rh   )paramsc              3      K   | ]r  }t        |j                        d k(  xrT t        |j                  d   dd      xr8 t        |j                  d   dd       xr t        |j                  d   dd        t yw)   r   textNfunction_callfunction_response)lenrS   getattr).0msgs     r&   	<genexpr>zDGeminiLLMAdapter._from_universal_context_messages.<locals>.<genexpr>  s      #

 	 		Na E		!fd3ECIIaL/4@@E CIIaL*=tDDE#
s   A8A:userru   rolerS   )r   r   )r9   r   r\   r:   rI   rG   _from_standard_messagern   rm   rf   r   rh   update%_apply_thought_signatures_to_messages'_merge_parallel_tool_calls_for_thinkinganyr   r   rS   rc   )
r,   ro   r   r   rh   thought_signature_dictsr\   r=   has_regular_messagesms
             r&   r0   z1GeminiLLMAdapter._from_universal_context_messages   s   * "')$"$ 2 $	YG
 '#56w5++F37JJ+227??C 0 0033489K4L1M 4  1 F ~~/**%+%>%>" 22,33F4W4WXI$	YN 	223JHU ??@WYab  # #

  #
  
 &:OOGBT8U7VWX  (3!177A33%%xL^%__ 4s   F7F7r\   rr   c                *   |d   }|j                  dg       }|dk(  r`|j                  rd}nXd}t        |t              r|}n(t        |t              rdj                  d |D              }|r| j                  |      S |d	k(  rd
}g }i }|j                  d      r^|d   D ]T  }|d   }	|d   d   }
|
||	<   |j                  t        t        |	|
t        j                  |d   d                            V nb|dk(  rd}	 t        j                  |d         }t        |t              r|}nd|i}|j                  d      }d}|r||j                  v r|j                  |   }|j                  t        t        |||                   nt        |t              r|j                  t        |             nt        |t              r|D ]  }|d   dk(  r|j                  t        |d                +|d   dk(  r|d   d   j!                  d      rz|d   d   }|j#                  d      d   j#                  d       d!   }|j                  t        t%        |t'        j(                  |j#                  d"      d         #      $             |d   dk(  r!|d   d   }t+        j,                  d%|        |d   d&k(  rE|d&   }t'        j(                  |d'         }|j                  t        t%        d(|#      $             :|d   d)k(  sD|d)   }|j                  t        t/        |j                  d*      |j                  d+      ,      -              | j                  t1        ||.      |/      S # t        $ r}d|d   i}Y d}~_d}~ww xY w)0a(  Convert standard universal context message to Google Content object.

        Handles conversion of text, images, and function calls to Google's
        format.
        System instructions are returned as a plain string.

        Args:
            message: Message in standard universal context format.
            already_have_system_instruction: Whether we already have a system instruction
            params: Parameters for conversion.

        Returns:
            MessageConversionResult containing either a Content object or a
            system instruction string.

        Examples:
            Standard text message::

                {
                    "role": "user",
                    "content": "Hello there"
                }

            Converts to Google Content with::

                Content(
                    role="user",
                    parts=[Part(text="Hello there")]
                )

            Standard function call message::

                {
                    "role": "assistant",
                    "tool_calls": [
                        {
                            "function": {
                                "name": "search",
                                "arguments": '{"query": "test"}'
                            }
                        }
                    ]
                }

            Converts to Google Content with::

                Content(
                    role="user",
                    parts=[Part(function_call=FunctionCall(name="search", args={"query": "test"}))]
                )
        r   rf   systemr}   N c              3   P   K   | ]  }|j                  d       dk(  s|d      yw)rq   ru   NrI   rz   r^   s     r&   r|   z:GeminiLLMAdapter._from_standard_message.<locals>.<genexpr>Y  s*      2)-DHHV<LPV<VV2s   &
&)r   	assistantmodel
tool_callsidfunctionname	arguments)r   r   args)rv   toolr?   tool_call_idtool_call_result)r   r   response)rw   r~   rq   ru   	image_urlurlzdata::rt   ;r   ,)	mime_typerV   )rT   zUnsupported 'image_url': input_audiorV   z	audio/wav	file_datar   file_uri)r   r   )r   r   )rf   rh   )rI   rl   r9   r"   r<   joinri   rG   r   r   jsonloadsr:   rY   rh   r   
startswithsplitr   base64	b64decoder
   warningr   r   )r,   r\   rr   r   rf   r   rS   rh   tcr   r   r   response_dictr_   r   function_namecr   r   r   audio_bytesr   s                         r&   r   z'GeminiLLMAdapter._from_standard_message  s   l v++i,855*."gs+)0&.), 2182 *& &77K]7^^[ D')$;;|$l+ X*~f-37,R0&2!!%!%BzN;,G!H'	 V^D	>::gi&89h-$,M%,h$7M #;;~6L.M0S0S S & C CL QLL&6'*!.' %LL7+,& V9&LL1V9!56vY+-!K.2G2R2RSZ2[K./C #		#q 1 7 7 <Q ?ILL(,*3%+%5%5ciinQ6G%H) vY+-K./CNN%>se#DEvY-/"#M"2K"("2"2;v3F"GKLL$S^2_!`avY+- !+ILL&.*3--*D)2z)B'1B ++U3)E , 
 	
q  > ")')*< =>s   ?/M7 7	N NNr   r   c                 ~   |s|S t        d |D              }|s|S dt        dt        fd}dt        dt        fd}g }d}|t        |      k  r||   } ||      r ||      rt	        |j
                        }	g }
|dz   }|t        |      k  r\||   } ||      r* ||      rnF|	j                  |j
                         |dz  }n|
j                  |       |dz  }|t        |      k  r\|j                  t        d|		             |j                  |
       |}n|j                  |       |dz  }|t        |      k  r|S )
a,  Merge parallel tool calls into single Content objects when thinking is enabled.

        Gemini expects parallel tool calls (multiple function calls made
        simultaneously) to be in a single Content with multiple function_call
        Parts. This method takes a list of Content messages, where parallel
        tool calls may be split across multiple messages, and merges them into
        single messages.

        This only has an effect when thought_signatures are present (i.e., when
        thinking is enabled). When thinking is disabled, merging doesn't matter.
        When thinking is enabled, there is a guarantee that the first tool call
        (and only the first) in any batch of parallel tool calls will have a
        thought_signature. This allows us to distinguish:

        - Parallel tool calls: share a single thought_signature (on the first call)
        - Sequential tool calls: each have their own thought_signature

        Algorithm: A tool call message with a thought_signature starts a new
        parallel group. Any tool call messages after it without a
        thought_signature get merged into that group, regardless of what
        messages appear in between.

        Args:
            thought_signature_dicts: A list of thought signature dicts, used
                to determine if the work of merging is necessary.
            messages: List of Content messages to process.

        Returns:
            List of Content messages with parallel tool calls merged when
            thought_signatures are present, otherwise unchanged.
        c              3   ^   K   | ]%  }|j                  d i       j                  d       ' yw)bookmarkrv   Nr   )rz   tss     r&   r|   zKGeminiLLMAdapter._merge_parallel_tool_calls_for_thinking.<locals>.<genexpr>  s+      +
<>BFF:r"&&7+
s   +-r{   r)   c                 x    | j                   dk(  xr* | j                  xr t        d | j                  D              S )z3Check if message contains only function_call parts.r   c              3   6   K   | ]  }t        |d d        yw)rv   Nry   r   s     r&   r|   ziGeminiLLMAdapter._merge_parallel_tool_calls_for_thinking.<locals>.is_tool_call_message.<locals>.<genexpr>  s     Sot<S   )r   rS   allr{   s    r&   is_tool_call_messagezVGeminiLLMAdapter._merge_parallel_tool_calls_for_thinking.<locals>.is_tool_call_message  s:     G# TIITSSSr%   c                 :    t        d | j                  D              S )z9Check if any part in the message has a thought_signature.c              3   6   K   | ]  }t        |d d        yw)rW   Nr   r   s     r&   r|   zrGeminiLLMAdapter._merge_parallel_tool_calls_for_thinking.<locals>.message_has_thought_signature.<locals>.<genexpr>  s     VDwt%8$?Vr   )r   rS   r   s    r&   message_has_thought_signaturez_GeminiLLMAdapter._merge_parallel_tool_calls_for_thinking.<locals>.message_has_thought_signature  s    VCIIVVVr%   r   rt   r   r   )r   r   rm   rx   r<   rS   extendrG   )r,   r   r   has_function_call_signaturesr   r   merged_messagesicurrentmerged_partsother_messagesjnext_msgs                r&   r   z8GeminiLLMAdapter._merge_parallel_tool_calls_for_thinking  so   D O (+ +
BY+
 (
$ ,O	g 	$ 		Ww 	W4 	W #h-qkG $G,1Nw1W#GMM2!#E #h-''{H+H58B! )//?FA '--h7Q #h-'   &&wG<'PQ&&~6&&w/Q? #h-B r%   Nc                 n   |syt        j                  dt        |              |D ]  }|j                  d      }|j                  d      rt        j                  d|d           A|j                  d      r6|d   }t        |      dkD  r|dd  dn|}t        j                  d	|        |j                  d
      st        j                  d        |D cg c]#  }t        |t              r|j                  dk(  r|% }}d}	d}
|D ]  }|j                  d      }|j                  d      }|r|s*t        |
t        |            D ]H  }||   }|j                  s|j                  d   }| j                  ||      s7||_        |	dz  }	|dz   }
   t        j                  d|	 d       yc c}w )a  Apply thought signatures to corresponding assistant messages.

        See GoogleLLMService for more details about thought signatures.

        Args:
            thought_signature_dicts: A list of dicts containing:
                - "signature": a thought signature
                - "bookmark": a bookmark to identify the message part to apply the signature to.
                  The bookmark may contain one of:
                    - "function_call" (a function call ID string)
                    - "text" (a text string)
                    - "inline_data" (a Blob)
                The list of thought signature dicts is in order.
            messages: List of messages to apply the thought signatures to.
        NzThought signatures to apply: r   rv   z - To function call: ru   2   rU   z - To text: rT   z - To inline datar   r   	signaturert   zApplied z thought signatures.)r
   rZ   rx   rI   tracer9   r   r   rangerS   (_thought_signature_bookmark_matches_partrW   )r,   r   r   r   r   ru   log_display_textr\   assistant_messagesthought_signatures_appliedmessage_start_indexthought_signature_dictr   r   	last_parts                  r&   r   z6GeminiLLMAdapter._apply_thought_signatures_to_messages  s   $ ' 	4S9P5Q4RST) 		3Bvvj)H||O,4Xo5N4OPQf%'8;D	Bd3Bi[#4D |,<+=>?m,02		3 $
'7+0G 
 
 &'"&= 	".22;?I-11*=HH .4F0GH ,Q/}} $MM"-	 @@9U2;I/.!3. +,a%'!	4 	x :;;OPQI
s   (F2r   r^   c                    |j                  d      x}r| j                  ||      S |j                  d      x}r| j                  ||      S |j                  d      x}r| j                  ||      S t	        j
                  d|        y)Nrv   ru   rT   z)Unknown thought signature bookmark type: F)rI   6_thought_signature_function_call_bookmark_matches_part-_thought_signature_text_bookmark_matches_part4_thought_signature_inline_data_bookmark_matches_partr
   r   )r,   r   r^   function_call_bookmarktext_bookmarkrT   s         r&   r   z9GeminiLLMAdapter._thought_signature_bookmark_matches_parta  s    %-\\/%BB!BNN&  'll622]2EEmUYZZ$LL77[7LL[Z^__NNFxjQRr%   bookmark_function_call_idc                     t        |d      r>|j                  r2|j                  j                  |k(  rt        j                  d|        yy)Nrv   z'Thought signature function call match: TF)hasattrrv   r   r
   r   )r,   r   r^   s      r&   r   zGGeminiLLMAdapter._thought_signature_function_call_bookmark_matches_parto  sH     D/*""""%%)BBLLBC\B]^_r%   bookmark_textc                    t        |d      r|j                  rdj                  |j                               }dj                  |j                  j                               }||k(  s"|j	                  |      s|j	                  |      rOt        |j                        dkD  r|j                  d d  dn|j                  }t        j                  d|        yy)Nru   r   r   rU   zThought signature text match: TF)r   ru   r   r   r   rx   r
   r   )r,   r   r^   	part_textr   s        r&   r   z>GeminiLLMAdapter._thought_signature_text_bookmark_matches_part|  s    4 TYYHH]%8%8%:;M!23I ]* ++I6''6=@^b=Pdiin%5S#9VZV_V_ =>N=OPQr%   bookmark_inline_datac                     t        |d      rW|j                  rKt        |j                  j                        t        |j                        k(  rt	        j
                  d       yy)NrT   z#Thought signature inline data matchTF)r   rT   rx   rV   r
   r   )r,   r   r^   s      r&   r   zEGeminiLLMAdapter._thought_signature_inline_data_bookmark_matches_part  sQ     D-(   D$$))*c2F2K2K.LLLL>@r%   )#r   r   r    r!   propertyr"   r-   r   r   r3   r   r   r   r   rQ   r`   r   rc   ri   rn   r   r0   r   r   r:   r   r   r   r   rm   r   r   r   r   r   r$   r%   r&   r(   r(   *   s    c  
 
@Y 
"8>[ 8>T$sTWx.EY 8>t$
 $tDcN?S $< 1 1 1 S S S 5 5 5W`*./@*AW`	W`r^
)^
6M^
	 ^
@_'+Dz_=A']_	g_BGR'+DzGR=A']GR	GRR T VZ ),48	3 VZ _c ($(04	r%   r(   )'r!   r   r   dataclassesr   r   typingr   r   r   r   r	   logurur
   openair   !pipecat.adapters.base_llm_adapterr   %pipecat.adapters.schemas.tools_schemar   r   *pipecat.processors.aggregators.llm_contextr   r   r   r   google.genai.typesr   r   r   r   r   r   ModuleNotFoundErrorr_   errorrY   r   r(   r$   r%   r&   <module>r      s    &   ( 7 7   < J ,`` 	  t	~&?@ t	  ,FLL;qc"#FLL[\
&qc*
++,s   A2 2B.72B))B.