o
    ZhY                     @   s  d dl Z d dlZd dl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 d dl	m
Z
 d dlmZ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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$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+ d dl,m-Z-m.Z. d dl/m0Z0m1Z1m2Z2 d dl3m4Z4m5Z5 d dl6m7Z7 d dl8m9Z9m:Z:m;Z; e<e=Z>dZ?dZ@de#deAfddZBdeeCef de#fddZDdeeCef dee$ de$fddZEG dd deZFG dd dZGdS )     N)datetime)Queue)mktime)	AnyDict	GeneratorIteratorListMappingOptionalTypecast)	urlencodeurlparse
urlunparse)format_date_time)CallbackManagerForLLMRun)BaseChatModelgenerate_from_stream)	AIMessageAIMessageChunkBaseMessageBaseMessageChunkChatMessageChatMessageChunkFunctionMessageChunkHumanMessageHumanMessageChunkSystemMessageToolMessageChunk)make_invalid_tool_callparse_tool_call)ChatGenerationChatGenerationChunk
ChatResult)get_from_dict_or_envget_pydantic_field_names)
get_fields)
ConfigDictFieldmodel_validatorz$wss://spark-api.xf-yun.com/v3.5/chatzgeneralv3.5messagereturnc                 C   s   t | trd| jd}|S t | trd| jd}|S t | trSd| jd}d| jv r;| jd |d< |d dkr;d |d< d| jv rQ| jd |d< |d dkrQd |d< |S t | tr`d| jd}|S td	|  )
Nuser)rolecontent	assistantfunction_callr/    
tool_callssystemzGot unknown type )
isinstancer   r/   r   r   additional_kwargsr   
ValueError)r+   Zmessage_dict r8   _/var/www/html/lang_env/lib/python3.10/site-packages/langchain_community/chat_models/sparkllm.pyconvert_message_to_dict9   s*   





r:   _dictc                 C   s   | d }| d }|dkrt |dS |dkrpg }i }| d }r&t||d< g }| d }rb||d< | d D ])}z|t|dd	 W q7 ty` }	 z|t|t|	 W Y d }	~	q7d }	~	ww ni }|pgd
}
t|
|||dS |dkryt	|dS t
||dS )Nr.   r/   r-   r/   r0   r1   r3   T)Z	return_idr2   )r/   r6   r3   invalid_tool_callsr4   r/   r.   )r   getdictappendr!   	Exceptionr    strr   r   r   )r;   msg_rolemsg_contentr=   r6   r1   r3   Zraw_tool_callsZraw_tool_caller/   r8   r8   r9   convert_dict_to_messageS   sB   

rG   default_classc                 C   s  t t| d}t t| dpd}i }| dr1t| d }d|v r-|d d u r-d|d< ||d< | dr<| d |d< |dksD|tkrIt|dS |d	ksQ|tkrWt||d
S |dks_|tkrgt|| d dS |dkso|tkrwt|| d dS |s}|tkrt||dS ||dS )Nr.   r/   r2   r1   namer3   r-   r<   r0   )r/   r6   function)r/   rI   Ztooltool_call_id)r/   rK   r>   )	r   rC   r?   r@   r   r   r   r   r   )r;   rH   rD   rE   r6   r1   r8   r8   r9   _convert_delta_to_message_chunkv   s*   



rL   c                   @   s  e Zd ZU dZedefddZedee	e	f fddZ
dZeed< edd	d
Zee	 ed< 	 eddd
Zee	 ed< 	 eddd
Zee	 ed< 	 eddd
Zee	 ed< 	 eddd
Zee	 ed< 	 dZe	ed< dZeed< 	 edddZeed< 	 eddZeed< 	 dZeed < 	 eed!Zee	ef ed"< 	 ed#d$Ze d%d&ed'edefd(d)Z!e d%d&ed'ee	ef defd*d+Z"		d7d,e#e$ d-ee#e	  d.ee% d/ede&e' f
d0d1Z(			d8d,e#e$ d-ee#e	  d.ee% d2ee d/ede)fd3d4Z*ede	fd5d6Z+dS )9ChatSparkLLMu-  IFlyTek Spark chat model integration.

    Setup:
        To use, you should have the environment variable``IFLYTEK_SPARK_API_KEY``,
        ``IFLYTEK_SPARK_API_SECRET`` and ``IFLYTEK_SPARK_APP_ID``.

    Key init args — completion params:
        model: Optional[str]
            Name of IFLYTEK SPARK model to use.
        temperature: Optional[float]
            Sampling temperature.
        top_k: Optional[float]
            What search sampling control to use.
        streaming: Optional[bool]
             Whether to stream the results or not.

    Key init args — client params:
        api_key: Optional[str]
            IFLYTEK SPARK API KEY. If not passed in will be read from env var IFLYTEK_SPARK_API_KEY.
        api_secret: Optional[str]
            IFLYTEK SPARK API SECRET. If not passed in will be read from env var IFLYTEK_SPARK_API_SECRET.
        api_url: Optional[str]
            Base URL for API requests.
        timeout: Optional[int]
            Timeout for requests.

    See full list of supported init args and their descriptions in the params section.

    Instantiate:
        .. code-block:: python

            from langchain_community.chat_models import ChatSparkLLM

            chat = ChatSparkLLM(
                api_key="your-api-key",
                api_secret="your-api-secret",
                model='Spark4.0 Ultra',
                # temperature=...,
                # other params...
            )

    Invoke:
        .. code-block:: python

            messages = [
                ("system", "你是一名专业的翻译家，可以将用户的中文翻译为英文。"),
                ("human", "我喜欢编程。"),
            ]
            chat.invoke(messages)

        .. code-block:: python

            AIMessage(
                content='I like programming.',
                response_metadata={
                    'token_usage': {
                        'question_tokens': 3,
                        'prompt_tokens': 16,
                        'completion_tokens': 4,
                        'total_tokens': 20
                    }
                },
                id='run-af8b3531-7bf7-47f0-bfe8-9262cb2a9d47-0'
            )

    Stream:
        .. code-block:: python

            for chunk in chat.stream(messages):
                print(chunk)

        .. code-block:: python

            content='I' id='run-fdbb57c2-2d32-4516-b894-6c5a67605d83'
            content=' like programming' id='run-fdbb57c2-2d32-4516-b894-6c5a67605d83'
            content='.' id='run-fdbb57c2-2d32-4516-b894-6c5a67605d83'

        .. code-block:: python

            stream = chat.stream(messages)
            full = next(stream)
            for chunk in stream:
                full += chunk
            full

        .. code-block:: python

            AIMessageChunk(
                content='I like programming.',
                id='run-aca2fa82-c2e4-4835-b7e2-865ddd3c46cb'
            )

    Response metadata
        .. code-block:: python

            ai_msg = chat.invoke(messages)
            ai_msg.response_metadata

        .. code-block:: python

            {
                'token_usage': {
                    'question_tokens': 3,
                    'prompt_tokens': 16,
                    'completion_tokens': 4,
                    'total_tokens': 20
                }
            }

    r,   c                 C      dS )z9Return whether this model can be serialized by Langchain.Fr8   )clsr8   r8   r9   is_lc_serializable  s   zChatSparkLLM.is_lc_serializablec                 C   s   ddddddS )NIFLYTEK_SPARK_APP_IDIFLYTEK_SPARK_API_KEYIFLYTEK_SPARK_API_SECRETIFLYTEK_SPARK_API_URLIFLYTEK_SPARK_LLM_DOMAIN)spark_app_idspark_api_keyspark_api_secretspark_api_urlspark_llm_domainr8   selfr8   r8   r9   
lc_secrets  s   zChatSparkLLM.lc_secretsNclientapp_id)defaultaliasrV   api_keyrW   
api_secretrX   api_urlrY   modelrZ   Zlc_userspark_user_idF	streaming   timeout)ra   request_timeoutg      ?r`   temperature   top_k)default_factorymodel_kwargsT)Zpopulate_by_namebefore)modevaluesc                 C   s   t |ddgd|d< t |ddgd|d< t |ddgd	|d< t |d
dt|d
< t |ddt|d< dd t|  D }|d|d d< |d|d d< t|d |d |d |d
 |d |d d|d< |S )NrV   r_   rQ   rW   rb   rR   rX   rc   rS   rY   rT   rZ   rU   c                 S   s"   i | ]\}}|j d ur||j qS Nrk   ).0rI   fieldr8   r8   r9   
<dictcomp>O  s
    
z5ChatSparkLLM.validate_environment.<locals>.<dictcomp>rl   rp   rn   )r_   rb   rc   rd   spark_domainrp   r^   )r%   SPARK_API_URLSPARK_LLM_DOMAINr'   itemsr?   _SparkLLMClient)rO   rs   default_valuesr8   r8   r9   validate_environment/  sR   

z!ChatSparkLLM.validate_environmentc              
   C   s   t | }|di }t|D ](}||v rtd| d||vr6td| d| d| d ||||< q|| }|rHtd| d	||d< |S )
z>Build extra kwargs from additional params that were passed in.rp   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&   r?   listr7   loggerwarningpopintersectionkeys)rO   rs   Zall_required_field_namesextra
field_nameZinvalid_model_kwargsr8   r8   r9   build_extrae  s,   
zChatSparkLLM.build_extramessagesstoprun_managerkwargsc           
      k   s    t }| jjdd |D | j| jdd | jj| jdD ]$}d|vr#q|d }t||}t|d}	|r=|j	t
|j|	d |	V  qd S )	Nc                 S      g | ]}t |qS r8   r:   ru   mr8   r8   r9   
<listcomp>      z(ChatSparkLLM._stream.<locals>.<listcomp>T)rg   ri   datar+   )chunk)r   r^   arunrf   rp   	subscriberj   rL   r#   Zon_llm_new_tokenrC   r/   )
r\   r   r   r   r   Zdefault_chunk_classr/   deltar   Zcg_chunkr8   r8   r9   _stream  s$   

zChatSparkLLM._streamstreamc                 K   s   |s| j r| jd|||d|}t|S | jdd |D | j| jd i }i }| jj| jdD ]}	d|	v r=|	d |d< d|	vrBq1|	d }q1t	|}
t
|
d	g}t||d
S )N)r   r   r   c                 S   r   r8   r   r   r8   r8   r9   r     r   z*ChatSparkLLM._generate.<locals>.<listcomp>Fr   usageZtoken_usager   r   )generations
llm_outputr8   )rg   r   r   r^   r   rf   rp   r   rj   rG   r"   r$   )r\   r   r   r   r   r   Zstream_iter
completionr   r/   r+   r   r8   r8   r9   	_generate  s0   

zChatSparkLLM._generatec                 C   rN   )Nzspark-llm-chatr8   r[   r8   r8   r9   	_llm_type  s   zChatSparkLLM._llm_type)NNNNN),__name__
__module____qualname____doc__classmethodboolrP   propertyr   rC   r]   r^   r   __annotations__r)   rV   r   rW   rX   rY   rZ   rf   rg   rj   intrl   floatrn   r@   rp   r(   Zmodel_configr*   r~   r   r	   r   r   r   r#   r   r$   r   r   r8   r8   r8   r9   rM      s   
 o	4



 rM   c                   @   s^  e Zd ZdZ			d)dedededee dee dee fd	d
ZededededefddZ			d*de
e dedee deddf
ddZ		d*de
e dedee dedejf
ddZdedee ddfddZdedededdfddZdeddfddZded eddfd!d"Z	d+dededee defd#d$Zd,d&ee deeddf fd'd(ZdS )-r|   z
    Use websocket-client to call the SparkLLM interface provided by Xfyun,
    which is the iFlyTek's open platform for AI capabilities
    Nr_   rb   rc   rd   rx   rp   c                 C   sr   z	dd l }|| _W n ty   tdw |stn|| _|| _|| _|p%t| _t	 | _
ddd| _|| _|| _d S )Nr   zhCould not import websocket client python package. Please install it with `pip install websocket-client`.r2   r0   r>   )	websocketwebsocket_clientImportErrorry   rd   r_   rp   rz   rx   r   queueblocking_messagerb   rc   )r\   r_   rb   rc   rd   rx   rp   r   r8   r8   r9   __init__  s    	


z_SparkLLMClient.__init__r,   c                 C   s   t tt  }t| }|j}|j}d| d| d| d}tj	|
d|
dtjd }t|jdd}	d| d	|	 d
}
t|

djdd}|||d}t|}t|j|j|j|j||jf}|S )zK
        Generate a request url with an api key and an api secret.
        zhost: z
date: z
GET z	 HTTP/1.1zutf-8)	digestmod)encodingz	api_key="zQ", algorithm="hmac-sha256",         headers="host date request-line", signature="")authorizationdatehost)r   r   r   now	timetupler   netlocpathhmacnewencodehashlibsha256digestbase64	b64encodedecoder   r   schemeparamsfragment)rd   rb   rc   r   
parsed_urlr   r   Zsignature_originZsignature_shaZsignature_sha_base64Zauthorization_originr   params_dictZencoded_paramsurlr8   r8   r9   _create_url  s>   
z_SparkLLMClient._create_urlFr   user_idrg   c                 C   sl   | j d | j jt| j| j| j| j| j	| j
| jd}||_||_|d u r*| jn||_||_|  d S )NF)
on_messageon_erroron_closeon_open)r   ZenableTraceZWebSocketAppr|   r   rd   rb   rc   r   r   r   r   r   r   rp   rg   run_forever)r\   r   r   rp   rg   wsr8   r8   r9   run  s"   z_SparkLLMClient.runc                 C   s$   t j| j||||fd}|  |S )N)targetargs)	threadingThreadr   start)r\   r   r   rp   rg   Z	ws_threadr8   r8   r9   r   '  s   	z_SparkLLMClient.arunr   errorc                 C   s   | j d|i |  d S )Nr   )r   putclose)r\   r   r   r8   r8   r9   r   :  s   z_SparkLLMClient.on_errorclose_status_codeclose_reasonc                 C   s(   t d||di | jddi d S )Nlog)r   r   doneT)r   debugr   r   )r\   r   r   r   r8   r8   r9   r   >  s   z_SparkLLMClient.on_closec                 C   s6   ddd| _ t| j|j|j|jd}|| d S )Nr2   r0   r>   )r   r   rp   )r   jsondumps
gen_paramsr   r   rp   send)r\   r   r   r8   r8   r9   r   I  s   z_SparkLLMClient.on_openr+   c           	      C   s  t |}|d d }|dkr'| jdd| d|d d  i |  d S |d d	 }|d
 }|d d d }|jrI| jd|d d i n	| jd  |7  < |dkr|jsb| jd| ji |rr|di di di ni }| jd|i |  d S d S )Nheadercoder   r   zCode: z	, Error: r+   payloadchoicesstatustextr/   r      r   )r   loadsr   r   r   rg   r   r?   )	r\   r   r+   r   r   r   r   r/   Z
usage_datar8   r8   r9   r   R  s.   
z_SparkLLMClient.on_messagec                 C   sP   | j |ddd| jiidd|iid}|r|d d | td|  |S )	N)r_   uidZchatdomainr+   r   )r   	parameterr   r   zSpark Request Parameters: )r_   rx   updater   r   )r\   r   r   rp   r   r8   r8   r9   r   m  s   

z_SparkLLMClient.gen_paramsrh   ri   c              
   c   s    	 z	| j j|d}W n t jy  } ztd| dd }~ww d|v r+t|d d|v r3|V  qd|v r9d S d|vr?d S |V  q)	NTr   z-SparkLLMClient wait LLM api response timeout z secondsr   r   r   r   )r   r?   EmptyTimeoutErrorConnectionError)r\   ri   r/   _r8   r8   r9   r   {  s*   
z_SparkLLMClient.subscriber   )NFrt   )rh   )r   r   r   r   rC   r   r@   r   staticmethodr   r	   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r8   r8   r8   r9   r|     sz    

0

	
&r|   )Hr   r   r   r   loggingr   r   r   r   timer   typingr   r   r   r   r	   r
   r   r   r   urllib.parser   r   r   Zwsgiref.handlersr   Zlangchain_core.callbacksr   Z*langchain_core.language_models.chat_modelsr   r   Zlangchain_core.messagesr   r   r   r   r   r   r   r   r   r   r   Z*langchain_core.output_parsers.openai_toolsr    r!   Zlangchain_core.outputsr"   r#   r$   Zlangchain_core.utilsr%   r&   Zlangchain_core.utils.pydanticr'   Zpydanticr(   r)   r*   	getLoggerr   r   ry   rz   r@   r:   rC   rG   rL   rM   r|   r8   r8   r8   r9   <module>   sJ    ,4
#

  0