
    Zǻif                       d dl m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 d d	lmZ d d
lmZmZmZ erd dlmZ  edede	f         Z ej@                  e!      Z"ddZ#d dZ$d!dZ%d"dZ&	 	 	 	 	 	 d#dZ'd$dZ(d%dZ)d&dZ*	 	 d'	 	 	 	 	 	 	 	 	 	 	 d(dZ+ G d ded      Z,eddd	 	 	 	 	 	 	 d)d       Z-y)*    )annotationsN)Mapping)TYPE_CHECKINGAnyCallableOptionalTypeVarUnion)	TypedDict)client)run_helpers)	warn_beta)dumps)InputTokenDetailsOutputTokenDetailsUsageMetadata)genaiCzgenai.Client)boundc                \    | j                         D ci c]  \  }}|	|| c}}S c c}}w )z%Remove `None` values from dictionary.)items)dkvs      X/opt/lhia/marcimex/agent/venv/lib/python3.12/site-packages/langsmith/wrappers/_gemini.py_strip_noner      s(    WWY8TQ!-AqD888s   
((c                X    d| v r&t        | d   t              st        | d         | d<   yyy)zFConvert `GenerateContentConfig` to `dict` for LangSmith compatibility.configN)
isinstancedictvars)kwargss    r   _convert_config_for_tracingr#   $   s5    6*VH-=t"Dx 01x #E    c                    t        | t        t        f      r| S t        | d      r| j	                         S t        | d      r| j                         S | S )zISerialize a Pydantic/model object to dict (or return as-is for dict/str).
model_dumpto_dict)r   r    strhasattrr&   r'   )objs    r   _to_dictr+   *   sG    #c{#
sL!~~sI{{}Jr$   c                t   | j                  d      }|s| S t        |t              rAd|dg| j                  d      d| j                         D ci c]  \  }}|dvs|| c}}S t        |t              r)t        d |D              rN|D cg c]  }d|d	 c}| j                  d      d| j                         D ci c]  \  }}|dvs|| c}}S g }|D ]  }t        |      }t        |t              s |j                  dd      }|j                  d	g       }g }	g }
|D ]  }t        |      }t        |t              r&|	j                  |       |
j                  d
|d       Et        |t              sVd
|v r1|d
   r,|	j                  |d
          |
j                  d
|d
   d       d|v rt        |d         }t        |t              s|j                  dd      }|j                  dd      }t        |t              r%t        j                  |      j                  d      }n|}|
j                  dd| d| ddd       (d|v r=|d   }|
j                  d|j                  d      |j                  di       dd       id|v sd|v sst        |j                  d      xs |j                  d            }t        |t              s|
j                  d|j                  d       |j                  d      |j                  d!i       d"d#        |
r$t        d$ |
D              rd%j                  |	      }n|
r|
nd&}|j                  ||d        || j                  d      d| j                         D ci c]  \  }}|dvs|| c}}S | S c c}}w c c}w c c}}w c c}}w )'u  Process Gemini inputs to normalize them for LangSmith tracing.

    Example:
        ```txt
        {"contents": "Hello", "model": "gemini-pro"}
        → {"messages": [{"role": "user", "content": "Hello"}], "model": "gemini-pro"}
        {"contents": [{"role": "user", "parts": [{"text": "What is AI?"}]}], "model": "gemini-pro"}
        → {"messages": [{"role": "user", "content": "What is AI?"}], "model": "gemini-pro"}
        ```
    contentsuser)rolecontentmodel)messagesr1   )r-   r1   c              3  <   K   | ]  }t        |t                y w)N)r   r(   ).0items     r   	<genexpr>z)_process_gemini_inputs.<locals>.<genexpr>P   s     :z$$:s   r/   partstexttyper8   inline_data	mime_type
image/jpegdatar$   utf-8	image_urldata:;base64,highurldetailr:   r@   functionResponsefunction_responsenameresponse)rJ   rK   )r:   rI   function_callfunctionCallidargsrN   rJ   	argumentsr:   rL   c              3  D   K   | ]  }|j                  d       dk(    yw)r:   r8   N)get)r4   ps     r   r6   z)_process_gemini_inputs.<locals>.<genexpr>   s     $TQUU6]f%<$Ts    
 )rT   r   r(   r   listallr+   r    appendbytesbase64	b64encodedecodejoin)inputsr-   r   r   r5   r2   r0   r/   	raw_parts
text_partscontent_partspartr;   r<   r>   data_b64rI   fcmessage_contents                      r   _process_gemini_inputsrh   5   s    zz*%H (C "(X>?ZZ(
 "(RA1<Q3Q1R
 	
 (D!::: LTT4f>TG, &,\\^VTQq@U7UAqDV   K	HGw'Ggt,;;vv.DGR0I$&J24M! 7~dC(%%d+!((&$)GH!$-T>d6l%%d6l3!((&$v,)OP"d*"*4+>"?K%k48  +\ JI&??637D!$.#)#3#3D#9#@#@#I#'!(($/).yk('L*0* (4/(,-?(@%!(($7(9(=(=f(E,=,A,A*b,Q2 %,$0F!$((?";"Wtxx?WXB!"d+%,,(7*,&&,,.FF6N131C2"	]7t $Tm$T!TDHIIE 4A-bOOToFGWK	H\ !ZZ(
 "(RA1<Q3Q1R
 	
 MG S UVh Ss*   N#N#N)N.N.
N4N4c                :   t        |      }|j                  di       }t        |d      r'|j                  }t	        |dd      }t	        |dd      }n3|j                  d      }|j                  d      }|j                  d      }dd|j                  d      |||| d	S )
z*Extract invocation parameters for tracing.r   temperaturemax_output_tokensNstop_sequencesgooglechatr1   )ls_providerls_model_typels_model_namels_temperaturels_max_tokensls_stopls_invocation_params)r   rT   r)   rj   getattr)prepopulated_invocation_paramsr"   strippedr   rj   
max_tokensstops          r   _infer_invocation_paramsr{      s     6"H\\(B'F v}%((V%8$?
v/6jj/ZZ 34
zz*+  !g.%# > r$   c                ^   | j                  d      xs d}| j                  d      xs d}| j                  d      xs d}| j                  d      xs d}| j                  d      xs ||z   }i }|r)||d<   t        d|dz
        |d	<   t        d|dz
        |d
<   i }|r||d<   |rt        d|dz
        |d
<   t        |||t        di |j	                         D 	ci c]  \  }}	|		||	 c}	}t        di |j	                         D 	ci c]  \  }}	|		||	 c}	}      S c c}	}w c c}	}w )z2Convert Gemini usage metadata to LangSmith format.prompt_token_countr   candidates_token_countcached_content_token_countthoughts_token_counttotal_token_count
cache_readi@ cache_read_over_200k	over_200k	reasoning)input_tokensoutput_tokenstotal_tokensinput_token_detailsoutput_token_details )rT   maxr   r   r   r   )
gemini_usage_metadatar}   r~   r   r   r   r   r   r   r   s
             r   _create_usage_metadatar      s   .223GHMA2667OPUTU!!">?D1  1445KLQPQ!!"56 	7 66 
 !#!,FL)69)F27
23 ,/q2Dv2M+NK(!#,@[),/3IF3R,S[)',&- 
 3 9 9 ;M1q}q!tM
 0 
 4 : : <N1q!tN

 

 N Os   
D#$D#
D)D)c                   	 t        | d      r| j                         }n5t        | d      r| j                         }ndt        | dt	        |             i}d}g }d}d|v r{|d   ru|d   d   }d|v r|d   }d	|v rO|d	   rI|d	   D ]@  }d|v r%|d   r ||d   z  }|j                  d|d   d
       -d|v r|d   ~|d   }|j                  dd      }	|j                  dd      }
t        |
t              r%t        j                  |
      j                  d      }n|
}|j                  dd|	 d| ddd       d|v sd|v st        |j                  d      xs |j                  d            }t        |t              s|j                  d|j                  d      |j                  d      |j                  di       dd       C d|v r(|d   r#|d   }nd|v r|d   }|j                  d|d
       |D cg c]  }|j                  d      dk(  s| }}|rl|xs dd |t        |      D cg c]K  \  }}|d   j                  d      xs d!| d"||d   d   t        |d   d#         j                         d$d%M c}}d&}n(t!        |      d'kD  s|r|d   d   dk7  r|d |d(}n|d |d(}|j                  d)      }t#        ddd*      }|rt%        |      }|j                  d+      r|d   d ||d+   |d,S t        |d   t              r
|d   d ||d-S |d   d ||d-S c c}w c c}}w # t&        $ r&}t(        j+                  d.|        d/| icY d}~S d}~ww xY w)0z$Process Gemini response for tracing.r'   r&   r8   rW   N
candidatesr   r0   r7   r9   r;   r<   r=   r>   r$   r?   r@   rA   rB   rC   rD   rG   rL   rM   rN   rJ   rO   rP   rR   finish_reasonr:   	assistantcall_functionrQ   )rJ   rQ   )rN   r:   indexr   )r0   r/   r   
tool_calls   )r0   r/   r   usage_metadatar   r   r   r   )r0   r/   r   r   r   )r0   r/   r   r   z"Error processing Gemini response: output)r)   r'   r&   rv   r(   rZ   rT   r   r[   r\   r]   r^   r+   r    	enumerate_dumpslenr   r   	Exceptionloggerdebug)rK   rdictcontent_resultrc   r   	candidater0   rd   r;   r<   r>   re   rf   rU   r   itcresultr   
usage_dictes                        r   "_process_generate_content_responser      s!   N$8Y'$$&EX|,'')EWXvs8}EFE '+5 U<%8l+A.II%#I.g%''*: ' 0 ("!T>d6l*d6l:N)00&$v,1WX*d2tM7J7V*.}*=K(3\(RI#.??63#?D  *$6+1+;+;D+A+H+H+Q+/)00,716yk(/T282&!" -4$8N!) $ 9 UTXXn=U"B  *"d3 - 4 40?24&&,46FF6N9;9K:*%&	!"?("R #i/Io4N$-o$>Mu_"6]N  &.!IJ "/SA!%%-?2RaS
S *1T#!. "+:!6 2 !155d;Jqc{ *!"$&$7$?)/ "? 3K @*$fh	%	
	F& !#mA.v6&@ )#!.F *#!.F #34$1!!%

 /?J ::l# "),#!.$\2",  &+S1%i0'%2&0	   &i0'%2&0	 I TF  $9!=>(##$s]   EL. =L. A9L. ;L#L#L. 2AL(A9L. <L. 	L. #L. .	M7MMMc           	     d   | sdt        ddd      dS d}d}| D ],  }	 t        |d      r|j                  r||j                  z  }|}. t        ddd      }|r	 t        |d      r|j                  rt        |j                  d	      r|j                  j                         }nt        |j                  d
      r|j                  j                         }nqt        |j                  dd      t        |j                  dd      t        |j                  dd      t        |j                  dd      t        |j                  dd      d}t        |      }||dS # t        $ r#}t        j                  d|        Y d}~bd}~ww xY w# t        $ r"}t        j                  d|        Y d}~Zd}~ww xY w)z/Reduce streaming chunks into a single response.rW   r   r   )r0   r   Nr8   zError processing chunk: r   r'   r&   r}   r~   r   r   r   )r}   r~   r   r   r   z+Error extracting metadata from last chunk: )r   r)   r8   r   r   r   r   r'   r&   rv   r   )
all_chunks	full_text
last_chunkchunkr   r   r   s          r   _reduce_generate_content_chunksr     s   +aa
 	
 IJ 9	9uf%%**UZZ'	J	9 %2aa%N 	Lz#349R9R:44i@!+!:!:!B!B!DJZ66E!+!:!:!E!E!GJ /6&557KQ/ 3:&557OQR3 7>&557SUV7 18&557Mq1 .5&557JA."J$ "8
!C ( O  	9LL3A3788	9F  	LLLFqcJKK	Ls0   )EC6F 	FE<<F	F/F**F/Fc                     |xs i t        j                          fd       }t        j                          fd       }t        j                         r|S |S )z9Create a wrapper for Gemini's `generate_content` methods.c                     t        |       t        j                  ddrt        nd t        st
        nd t        j                  t              d}  |      | i |S Nllm)rJ   run_type	reduce_fnprocess_inputsprocess_outputs_invocation_params_fnr   	r#   r   	traceabler   rh   r   	functoolspartialr{   rO   r"   	decoratoris_streamingrJ   original_generaterw   textras      r   generatez_get_wrapper.<locals>.generate  ss     	$F+)) 
9E541:F2D"+"3"3(*H#
 
	 ,y*+T<V<<r$   c                    K   t        |       t        j                  ddrt        nd t        st
        nd t        j                  t              d}  |      | i | d {   S 7 wr   r   r   s      r   	ageneratez_get_wrapper.<locals>.agenerate  s}      	$F+)) 
9E541:F2D"+"3"3(*H#
 
	 2Y014B6BBBBs   A%A/(A-)A/)r   wrapsr   is_async)r   rJ   rw   tracing_extrar   r   r   r   s   ``` `  @r   _get_wrapperr     sf      bF__&'= (=( __&'C (C( $,,->?9MXMr$   c                  ,    e Zd ZU ded<   ded<   ded<   y)TracingExtrazOptional[Mapping[str, Any]]metadatazOptional[list[str]]tagszOptional[ls_client.Client]r   N)__name__
__module____qualname____annotations__r   r$   r   r   r     s    ))
&&r$   r   )totalChatGoogleGenerativeAI)r   	chat_namec                  |xs i }t        |j                  d      xs i       }|j                  di       }|j                         D ci c]  \  }}|dk7  s|| }}}|r||d<   t	        | d      rAt	        | j
                  d      r+t	        | j
                  j                  d      rt        d      t	        | d      rIt	        | j
                  d      r3t        | j
                  j                  |||d      | j
                  _        t	        | d      rIt	        | j
                  d	      r3t        | j
                  j                  |||d
      | j
                  _	        t	        | d      r}t	        | j                  d      rgt	        | j                  j
                  d      rGt        | j                  j
                  j                  |||d      | j                  j
                  _        t	        | d      r}t	        | j                  d      rgt	        | j                  j
                  d	      rGt        | j                  j
                  j                  |||d
      | j                  j
                  _	        | S c c}}w )a  Patch the Google Gen AI client to make it traceable.

    !!! warning

        **BETA**: This wrapper is in beta.

    Supports:
        - `generate_content` and `generate_content_stream` methods
        - Sync and async clients
        - Streaming and non-streaming responses
        - Tool/function calling with proper UI rendering
        - Multimodal inputs (text + images)
        - Image generation with `inline_data` support
        - Token usage tracking including reasoning tokens

    Args:
        client: The Google Gen AI client to patch.
        tracing_extra: Extra tracing information.
        chat_name: The run name for the chat endpoint.

    Returns:
        The patched client.

    Example:
        ```python
        from google import genai
        from google.genai import types
        from langsmith import wrappers

        # Use Google Gen AI client same as you normally would.
        client = wrappers.wrap_gemini(genai.Client(api_key="your-api-key"))

        # Basic text generation:
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents="Why is the sky blue?",
        )
        print(response.text)

        # Streaming:
        for chunk in client.models.generate_content_stream(
            model="gemini-2.5-flash",
            contents="Tell me a story",
        ):
            print(chunk.text, end="")

        # Tool/Function calling:
        schedule_meeting_function = {
            "name": "schedule_meeting",
            "description": "Schedules a meeting with specified attendees.",
            "parameters": {
                "type": "object",
                "properties": {
                    "attendees": {"type": "array", "items": {"type": "string"}},
                    "date": {"type": "string"},
                    "time": {"type": "string"},
                    "topic": {"type": "string"},
                },
                "required": ["attendees", "date", "time", "topic"],
            },
        }

        tools = types.Tool(function_declarations=[schedule_meeting_function])
        config = types.GenerateContentConfig(tools=[tools])

        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents="Schedule a meeting with Bob and Alice tomorrow at 2 PM.",
            config=config,
        )

        # Image generation:
        response = client.models.generate_content(
            model="gemini-2.5-flash-image",
            contents=["Create a picture of a futuristic city"],
        )

        # Save generated image
        from io import BytesIO
        from PIL import Image

        for part in response.candidates[0].content.parts:
            if part.inline_data is not None:
                image = Image.open(BytesIO(part.inline_data.data))
                image.save("generated_image.png")
        ```

    !!! version-added "Added in `langsmith` 0.4.33"

        Initial beta release of Google Gemini wrapper.

    r   ru   modelsgenerate_content__wrapped__zfThis Google Gen AI client has already been wrapped. Wrapping a client multiple times is not supported.F)r   r   generate_content_streamTaio)r    rT   popr   r)   r   r   
ValueErrorr   r   r   )r   r   r   r   rw   r   r   tracing_extra_rests           r   wrap_geminir     s"   F "'RM M%%j17R8H%-\\2H"%M" ',,.(A!z/1( ( )1:& 	!FMM#56FMM22MBA
 	
 vx WV]]<N%O)5MM***,*
& vx WV]]<U%V0<MM11*,1
- 	FJJ)FJJ%%'9:-9JJ..*,.


* 	FJJ)FJJ%%'@A4@JJ55*,5


1 M(s   
I2I2)r   r    returnr    )r"   r    r   None)r*   r   r   r   )r`   r    r   r    )rw   r    r"   r    r   r    )r   r    r   r   )rK   r   r   r    )r   rX   r   r    )NF)r   r   rJ   r(   rw   r    r   Optional[TracingExtra]r   boolr   r   )r   r   r   r   r   r(   r   r   ).
__future__r   r\   r   loggingcollections.abcr   typingr   r   r   r   r	   r
   typing_extensionsr   	langsmithr   	ls_clientr   #langsmith._internal._beta_decoratorr   langsmith._internal._orjsonr   r   langsmith.schemasr   r   r   rm   r   r   	getLoggerr   r   r   r#   r+   rh   r{   r   r   r   r   r   r   r   r$   r   <module>r      sL   "    #  ( ) ! 9 7 R RCu^S012			8	$9
2xv$(26	8&RP$f=H -14N4N
4N %)4N *	4N
 4N 4Nn'9E '  -1-	hh *h 	h
 h hr$   