U
    hm                     @  s  d Z ddlmZ ddlZddlZddlZddlZddlmZm	Z	m
Z
mZmZmZmZmZmZmZmZmZ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mZ dd	l m!Z! dd
l"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* ddl+m,Z,m-Z-m.Z. ddl/m0Z0m1Z1m2Z2 ddl3m4Z4 ddl5m6Z6m7Z7m8Z8 ddl9m:Z:m;Z; ddl<m=Z= erNddl>Z>e?e@ZAddddZBd.ddddddZCd/dddddddZDdd d!d"d#d$ZEd%d%d%d&d'd(ZFed)d*d+d,G d-d deZGdS )0zOpenAI chat wrapper.    )annotationsN)TYPE_CHECKINGAnyAsyncIteratorCallableDictIteratorListMappingOptionalSequenceTupleTypeUnion)
deprecated)AsyncCallbackManagerForLLMRunCallbackManagerForLLMRun)LanguageModelInput)BaseChatModelagenerate_from_streamgenerate_from_stream)create_base_retry_decorator)AIMessageChunkBaseMessageBaseMessageChunkChatMessageChunkFunctionMessageChunkHumanMessageChunkSystemMessageChunkToolMessageChunk)ChatGenerationChatGenerationChunk
ChatResult)	BaseModelFieldroot_validator)Runnable)get_from_dict_or_envget_pydantic_field_namespre_init)convert_dict_to_messageconvert_message_to_dict)is_openai_v1r   returnc                  C  s.   zdd l } W n tk
r(   tdY nX | S )Nr   zCould not import tiktoken python package. This is needed in order to calculate get_token_ids. Please install it with `pip install tiktoken`.)tiktokenImportError)r/    r1   J/tmp/pip-unpacked-wheel-9gdii04g/langchain_community/chat_models/openai.py_import_tiktokenE   s    
r3   
ChatOpenAIzHOptional[Union[AsyncCallbackManagerForLLMRun, CallbackManagerForLLMRun]]zCallable[[Any], Any])llmrun_managerr.   c                 C  s:   dd l }|jj|jj|jj|jj|jjg}t|| j|dS )Nr   )Zerror_typesmax_retriesr6   )	openaierrorTimeoutZAPIErrorZAPIConnectionErrorZRateLimitErrorZServiceUnavailableErrorr   r7   )r5   r6   r8   errorsr1   r1   r2   _create_retry_decoratorQ   s      r<   'Optional[AsyncCallbackManagerForLLMRun])r5   r6   kwargsr.   c                   sN   t  r jjf |I dH S t |d}|ddd fdd}|f |I dH S )z0Use tenacity to retry the async completion call.Nr6   r   r>   r.   c                    s    j jf | I d H S N)clientZacreater>   r5   r1   r2   _completion_with_retryp   s    z6acompletion_with_retry.<locals>._completion_with_retry)r,   async_clientcreater<   )r5   r6   r>   retry_decoratorrE   r1   rD   r2   acompletion_with_retrye   s    rI   zMapping[str, Any]zType[BaseMessageChunk]r   )_dictdefault_classr.   c                 C  s(  |  d}|  dpd}i }|  drVt| d }d|krN|d d krNd|d< ||d< |  drl| d |d< |dks||tkrt|dS |d	ks|tkrt||d
S |dks|tkrt|dS |dks|tkrt|| d dS |dks|tk rt|| d dS |s|tkrt||dS ||dS d S )Nrolecontent function_callnameZ
tool_callsuser)rM   Z	assistant)rM   additional_kwargssystemfunction)rM   rP   Ztooltool_call_id)rM   rU   )rM   rL   )getdictr   r   r   r   r   r   )rJ   rK   rL   rM   rR   rO   r1   r1   r2   _convert_delta_to_message_chunkx   s.    




rX   zUnion[int, dict])overall_token_usage	new_usager.   c                   s   t |tr8t  ts0tdt| dt  |  S t |tr~t  tshtdt| dt   fdd| D S tdt|  |S d S )Nz%Got different types for token usage: z and c                   s$   i | ]\}}|t  |d |qS )r   )_update_token_usagerV   ).0kvrY   r1   r2   
<dictcomp>   s    z'_update_token_usage.<locals>.<dictcomp>z!Unexpected type for token usage: )
isinstanceint
ValueErrortyperW   itemswarningswarn)rY   rZ   r1   r_   r2   r[      s     




r[   z0.0.10z1.0zlangchain_openai.ChatOpenAI)ZsinceZremovalZalternative_importc                      s  e Zd ZU dZeddddZeddddZed	dd
dZeddddZ	e
dddZded< e
dddZded< e
dddZded< dZded< e
edZd	ed< e
dddZd ed!< e
dd"dZd ed#< e
dd$dZd ed%< dZd ed&< e
dd'dZd(ed)< e
d*d+Zd,ed-< d.Zded/< d0Zd,ed1< dZd2ed3< dZd ed4< dZd5ed6< dZd7ed8< dZd9ed:< G d;d< d<Ze dd=d	d	d>d?d@Z!e"dAdAd>dBdCZ#ed	ddDdEZ$ddFdddGdHdIZ%dJdKdLdMdNZ&ddOdPdFddQdRdSdTZ'ddOdPdFdUddVdWdXdYZ(dOdPdZd[d\d]Z)d^dVd_d`daZ*ddOdPdbddcdRdddeZ+ddOdPdbdUddVdWdfdgZ,ed	ddhdiZ-ed	ddjdkZ.ddPdd	dl fdmdnZ/edddodpZ0dqddrdsZ1ddtdu fdvdwZ2dOd,dx fdydzZ3dd{d dd|d} fd~dZ4  Z5S )r4   a  `OpenAI` Chat large language models API.

    To use, you should have the ``openai`` python package installed, and the
    environment variable ``OPENAI_API_KEY`` set with your API key.

    Any parameters that are valid to be passed to the openai.create call can be passed
    in, even if not explicitly saved on this class.

    Example:
        .. code-block:: python

            from langchain_community.chat_models import ChatOpenAI
            openai = ChatOpenAI(model="gpt-3.5-turbo")
    zDict[str, str]r-   c                 C  s   ddiS )Nopenai_api_keyOPENAI_API_KEYr1   selfr1   r1   r2   
lc_secrets   s    zChatOpenAI.lc_secretsz	List[str]c                 C  s
   dddgS )z*Get the namespace of the langchain object.Z	langchainZchat_modelsr8   r1   clsr1   r1   r2   get_lc_namespace   s    zChatOpenAI.get_lc_namespacezDict[str, Any]c                 C  s8   i }| j r| j |d< | jr$| j|d< | jr4| j|d< |S )Nopenai_organizationopenai_api_baseopenai_proxy)rp   rq   rr   )rk   
attributesr1   r1   r2   lc_attributes   s    


zChatOpenAI.lc_attributesboolc                 C  s   dS )z9Return whether this model can be serialized by Langchain.Tr1   rm   r1   r1   r2   is_lc_serializable   s    zChatOpenAI.is_lc_serializableNT)defaultexcluder   rB   rF   gpt-3.5-turbomodel)rw   aliasstr
model_namegffffff?floattemperature)default_factorymodel_kwargsapi_keyzOptional[str]rh   base_urlrq   organizationrp   rr   timeoutz,Union[float, Tuple[float, float], Any, None]request_timeout   rw   rb   r7   F	streaming   nzOptional[int]
max_tokenstiktoken_model_namezUnion[Mapping[str, str], None]default_headersz!Union[Mapping[str, object], None]default_queryzUnion[Any, None]http_clientc                   @  s   e Zd ZdZdS )zChatOpenAI.ConfigTN)__name__
__module____qualname__Zallow_population_by_field_namer1   r1   r1   r2   Config  s   r   )pre)valuesr.   c              
   C  s   t | }|di }t|D ]P}||kr8td| d||krtd| d| d| d ||||< q|| }|rtd| d	||d< |S )
z>Build extra kwargs from additional params that were passed in.r   zFound z supplied twice.z	WARNING! z/ is not default parameter.
                    zJ was transferred to model_kwargs.
                    Please confirm that z is what you intended.zParameters za should be specified explicitly. Instead they were passed in as part of `model_kwargs` parameter.)	r(   rV   listrc   loggerwarningpopintersectionkeys)rn   r   Zall_required_field_namesextra
field_nameZinvalid_model_kwargsr1   r1   r2   build_extra  s"    
zChatOpenAI.build_extrar   c              	   C  sP  |d dk rt d|d dkr0|d r0t dt|dd|d< |d pZtd	pZtd
|d< |d pptd|d< t|dddd|d< zddl}W n tk
r   tdY nX t r4|d |d |d |d |d |d |d |d d}|ds|jf |j	j
|d< |dsL|jf |j	j
|d< n|dsL|j|d< n |S )z?Validate that api key and python package exists in environment.r   r   zn must be at least 1.r   zn must be 1 when streaming.rh   ri   rp   ZOPENAI_ORG_IDZOPENAI_ORGANIZATIONrq   ZOPENAI_API_BASErr   ZOPENAI_PROXYrN   r   r   NzTCould not import openai python package. Please install it with `pip install openai`.r   r7   r   r   r   )r   r   r   r   r7   r   r   r   rB   rF   )rc   r'   osgetenvr8   r0   r,   rV   ZOpenAIZchatZcompletionsZAsyncOpenAIZChatCompletion)rn   r   r8   Zclient_paramsr1   r1   r2   validate_environment0  s`      

zChatOpenAI.validate_environmentc                 C  sN   | j | j| j| jd| j}| jdk	r0| j|d< | jdk	rJt sJ| j|d< |S )z2Get the default parameters for calling OpenAI API.)rz   streamr   r   Nr   r   )r}   r   r   r   r   r   r   r,   )rk   paramsr1   r1   r2   _default_paramsk  s    


zChatOpenAI._default_paramsz"Optional[CallbackManagerForLLMRun])r6   r>   r.   c                   sB   t  r jjf |S t |d}|ddd fdd}|f |S )z*Use tenacity to retry the completion call.r?   r   r@   c                    s    j jf | S rA   )rB   rG   rC   rj   r1   r2   rE     s    z@ChatOpenAI.completion_with_retry.<locals>._completion_with_retry)r,   rB   rG   r<   )rk   r6   r>   rH   rE   r1   rj   r2   completion_with_retry{  s    z ChatOpenAI.completion_with_retryzList[Optional[dict]]rW   )llm_outputsr.   c           	      C  s   i }d }|D ]f}|d krq|d }|d k	r`|  D ],\}}||krVt|| |||< q2|||< q2|d kr|d}q|| jd}|r||d< |S )Ntoken_usagesystem_fingerprint)r   r}   )re   r[   rV   r}   )	rk   r   rY   r   outputr   r]   r^   Zcombinedr1   r1   r2   _combine_llm_outputs  s(     

zChatOpenAI._combine_llm_outputszList[BaseMessage]zOptional[List[str]]zIterator[ChatGenerationChunk])messagesstopr6   r>   r.   c                 k  s   |  ||\}}||ddi}t}| jf ||d|D ]}t|tsN| }t|d dkr`q8|d d }	|	d d krzq8t|	d |}|	d}
|
d k	rt|
dnd }|j}t	||d	}|r|j
|j|d
 |V  q8d S )Nr   Tr   r6   choicesr   deltafinish_reasonr   messagegeneration_info)chunk)_create_message_dictsr   r   ra   rW   lenrX   rV   	__class__r!   on_llm_new_tokentextrk   r   r   r6   r>   message_dictsr   Zdefault_chunk_classr   choicer   r   Zcg_chunkr1   r1   r2   _stream  s>     

 
 zChatOpenAI._streamzOptional[bool]r"   )r   r   r6   r   r>   r.   c                 K  s   |d k	r|n| j }|r6| j|f||d|}t|S | ||\}}	|	|d k	rXd|ini |}	| jf ||d|	}
| |
S N)r   r6   r   r   )r   r   r   r   r   _create_chat_resultrk   r   r   r6   r   r>   Zshould_streamZstream_iterr   r   responser1   r1   r2   	_generate  s.      zChatOpenAI._generatez+Tuple[List[Dict[str, Any]], Dict[str, Any]])r   r   r.   c                 C  s<   | j }|d k	r&d|krtd||d< dd |D }||fS )Nr   z2`stop` found in both the input and default params.c                 S  s   g | ]}t |qS r1   r+   r\   mr1   r1   r2   
<listcomp>  s     z4ChatOpenAI._create_message_dicts.<locals>.<listcomp>)_client_paramsrc   )rk   r   r   r   r   r1   r1   r2   r     s    z ChatOpenAI._create_message_dictszUnion[dict, BaseModel])r   r.   c           	      C  s   g }t |ts| }|d D ]J}t|d }t|dd}d|krR|d |d< t||d}|| q|di }|| j|dd	d
}t||dS )Nr   r   r   r   Zlogprobsr   usager   rN   )r   r}   r   )generations
llm_output)ra   rW   r*   rV   r    appendr}   r"   )	rk   r   r   resr   r   genr   r   r1   r1   r2   r     s&    

zChatOpenAI._create_chat_resultr=   z"AsyncIterator[ChatGenerationChunk]c                 K s   |  ||\}}||ddi}t}t| f||d|I d H 2 z3 d H W }t|ts\| }t|d dkrnq>|d d }	|	d d krq>t|	d |}|	d}
|
d k	rt|
dnd }|j}t	||d	}|r|j
|j|d
I d H  |V  q>6 d S )Nr   Tr   r   r   r   r   r   r   )tokenr   )r   r   rI   ra   rW   r   rX   rV   r   r!   r   r   r   r1   r1   r2   _astream  sB     
 
 zChatOpenAI._astreamc                   s   |d k	r|n| j }|r<| j|f||d|}t|I d H S | ||\}}	|	|d k	r^d|ini |}	t| f||d|	I d H }
| |
S r   )r   r   r   r   rI   r   r   r1   r1   r2   
_agenerate#  s2      zChatOpenAI._ageneratec                 C  s   d| j i| jS )zGet the identifying parameters.r}   )r}   r   rj   r1   r1   r2   _identifying_params=  s    zChatOpenAI._identifying_paramsc                 C  sP   d| j i}t s(|| j| j| jd | jrFddl}| j| jd|_| j	|S )z.Get the parameters used for the openai client.rz   )r   Zapi_baser   r   N)httphttps)
r}   r,   updaterh   rq   rp   rr   r8   proxyr   )rk   Zopenai_credsr8   r1   r1   r2   r   B  s     zChatOpenAI._client_params)r   r>   r.   c                   s   d| j it j|d| j|S )z,Get the parameters used to invoke the model.rz   )r   )r}   super_get_invocation_paramsr   )rk   r   r>   r   r1   r2   r   V  s     z!ChatOpenAI._get_invocation_paramsc                 C  s   dS )zReturn type of chat model.zopenai-chatr1   rj   r1   r1   r2   	_llm_typea  s    zChatOpenAI._llm_typezTuple[str, tiktoken.Encoding]c                 C  s|   t  }| jd k	r| j}n | j}|dkr,d}n|dkr8d}z||}W n, tk
rr   td d}||}Y nX ||fS )Nry   gpt-3.5-turbo-0301gpt-4z
gpt-4-0314z5Warning: model not found. Using cl100k_base encoding.Zcl100k_base)r3   r   r}   Zencoding_for_modelKeyErrorr   r   Zget_encoding)rk   Z	tiktoken_rz   encodingr1   r1   r2   _get_encoding_modelf  s    

zChatOpenAI._get_encoding_modelz	List[int])r   r.   c                   s0   t jd dkrt |S |  \}}||S )z9Get the tokens present in the text with tiktoken package.r      )sysversion_infor   get_token_idsr   encode)rk   r   _Zencoding_modelr   r1   r2   r   }  s    zChatOpenAI.get_token_ids)r   r.   c                   s   t jd dkrt |S |  \}}|dr:d}d}n.|dsN|drXd}d}ntd	| d
d}dd |D }|D ]D}||7 }| D ].\}	}
|t|	t
|
7 }|	dkr||7 }qq~|d7 }|S )zCalculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package.

        Official documentation: https://github.com/openai/openai-cookbook/blob/
        main/examples/How_to_format_inputs_to_ChatGPT_models.ipynbr   r   r      ry   r      zFget_num_tokens_from_messages() is not presently implemented for model zy.See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens.r   c                 S  s   g | ]}t |qS r1   r   r   r1   r1   r2   r     s     z;ChatOpenAI.get_num_tokens_from_messages.<locals>.<listcomp>rP   )r   r   r   get_num_tokens_from_messagesr   
startswithNotImplementedErrorre   r   r   r|   )rk   r   rz   r   Ztokens_per_messageZtokens_per_nameZ
num_tokensZmessages_dictr   keyvaluer   r1   r2   r     s,    

z'ChatOpenAI.get_num_tokens_from_messagesz:Sequence[Union[Dict[str, Any], Type[BaseModel], Callable]]z)Runnable[LanguageModelInput, BaseMessage])	functionsrO   r>   r.   c                   s   ddl m   fdd|D }|dk	r|t|dkr:td|d d |krhtd	| d
|d d  dd|i}|d|i}t jf d|i|S )a  Bind functions (and other objects) to this chat model.

        Args:
            functions: A list of function definitions to bind to this chat model.
                Can be  a dictionary, pydantic model, or callable. Pydantic
                models and callables will be automatically converted to
                their schema dictionary representation.
            function_call: Which function to require the model to call.
                Must be the name of the single provided function or
                "auto" to automatically determine which function to call
                (if any).
            kwargs: Any additional parameters to pass to the
                :class:`~langchain.runnable.Runnable` constructor.
        r   convert_to_openai_functionc                   s   g | ]} |qS r1   r1   )r\   fnr   r1   r2   r     s     z-ChatOpenAI.bind_functions.<locals>.<listcomp>Nr   zGWhen specifying `function_call`, you must provide exactly one function.rP   zFunction call z3 was specified, but the only provided function was .rO   r   )Z&langchain.chains.openai_functions.baser   r   rc   r   bind)rk   r   rO   r>   Zformatted_functionsZfunction_call_r   r   r2   bind_functions  s$    
zChatOpenAI.bind_functions)N)NN)NNN)NN)NNN)N)N)6r   r   r   __doc__propertyrl   classmethodro   rt   rv   r$   rB   __annotations__rF   r}   r   rW   r   rh   rq   rp   rr   r   r7   r   r   r   r   r   r   r   r   r%   r   r)   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   __classcell__r1   r1   r   r2   r4      s   
 
:   '     '    ( )N)N)Hr   
__future__r   loggingr   r   rf   typingr   r   r   r   r   r   r	   r
   r   r   r   r   r   Zlangchain_core._api.deprecationr   Zlangchain_core.callbacksr   r   Zlangchain_core.language_modelsr   Z*langchain_core.language_models.chat_modelsr   r   r   Z#langchain_core.language_models.llmsr   Zlangchain_core.messagesr   r   r   r   r   r   r   r   Zlangchain_core.outputsr    r!   r"   Zlangchain_core.pydantic_v1r#   r$   r%   Zlangchain_core.runnablesr&   Zlangchain_core.utilsr'   r(   r)   Z#langchain_community.adapters.openair*   r+   Z langchain_community.utils.openair,   r/   	getLoggerr   r   r3   r<   rI   rX   r[   r4   r1   r1   r1   r2   <module>   sD   <(

    