
    NgN                     v   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 ddl	m
Z
mZmZmZmZmZmZ ddlmZ e
rddlZddlmZ ddlmZ dd	lmZmZmZmZ dd
lmZ ddlm Z m!Z!m"Z" ddl#m$Z$m%Z%m&Z&  ej'        e(          Z) G d de$          Z* G d de$          Z+ G d de$          Z, G d de$          Z- G d de$          Z. G d de$          Z/ G d de$          Z0 G d de$          Z1 G d d          Z2 G d d           Z3 G d! d"e          Z4 G d# d$e$          Z5 G d% d&ee5                   Z6dS )'z Kinetica SQL generation LLM API.    N)version)Path)TYPE_CHECKINGAnyDictListOptionalPatterncast)pre_init)CallbackManagerForLLMRun)BaseChatModel)	AIMessageBaseMessageHumanMessageSystemMessage)BaseOutputParser)ChatGeneration
ChatResult
Generation)	BaseModel
ConfigDictFieldc                      e Zd ZU dZ edd          Zee         ed<    edd          Z	ee         ed<    eg d          Z
ee         ed	<    edd
          Zeee                  ed<    edd          Zee         ed<   defdZdS )_KdtSuggestContextpydantic API request typeNzName of tabledefaulttitletablezTable descriptiondescriptionzTable columns listcolumnszRules that apply to the table.rulesz)Samples that apply to the entire context.samplesreturnc                     g }|                     d| j         d           |                     d           | j        rt          | j                  dk    rt	          d           g }| j        D ]B}|                    dd                                          }|                     d|            C|                     d	                    |                     |                     d
           | j        r&|                     d| j         d| j         d           | j	        rXt          | j	                  dk    r@|                     d| j         d           | j	        D ]}|                     d|            d                    |          }|S )NzCREATE TABLE z AS(r   zcolumns list can't be null." z   z,
z);zCOMMENT ON TABLE z IS 'z';z-- When querying table z the following rules apply:z-- * 
)
appendr    r"   len
ValueErrorreplacestripjoinr!   r#   )selflinesr"   columnruleresults         d/var/www/html/ai-engine/env/lib/python3.11/site-packages/langchain_community/chat_models/kinetica.pyto_system_strz _KdtSuggestContext.to_system_str1   s   4TZ444555S| 	6s4<00A554555l 	+ 	+F^^C,,2244FNN>>>****UZZ(()))T 	TLLRTZRRd>NRRRSSS: 	-#dj//A--LLQ$*QQQ   
 - -^T^^,,,,5!!    )__name__
__module____qualname____doc__r   r    r	   str__annotations__r!   r"   r   r#   r$   r   r7    r8   r6   r   r   $   s        ## 5_EEEE8C=EEE!&t;N!O!O!OK#OOOr1EFFFGT#YFFF!&<" " "E8DI    $eG  GXd^   s      r8   r   c                   t    e Zd ZU dZdZee         ed<   ee	         ed<   defdZ
dee         fdZdefdZdS )	_KdtSuggestPayloadr   Nquestioncontextr%   c                     g }| j         D ]3}|j        
|                                }|                    |           4d                    |          S )Nz

)rC   r    r7   r+   r0   )r1   r2   table_contextcontext_strs       r6   get_system_strz!_KdtSuggestPayload.get_system_strT   s\    !\ 	& 	&M"*'5577KLL%%%%{{5!!!r8   c                 ,   g }| j         D ]}|j        
|j                                        D ]e\  }}|                    dd          }|                    t          d|pd                     |                    t          d|                     f|S )Nz'''userr)   rolecontent	assistant)rC   r$   itemsr.   r+   dict)r1   messagesrC   rB   answers        r6   get_messagesz_KdtSuggestPayload.get_messages]   s    | 	H 	HG&$+O$9$9$;$; H H &c22&(.b I I IJJJ+v F F FGGGGH r8   c                 :   g }|                     t          d|                                                      |                    |                                            |                     t          d| j        pd                     t          |          }|S )NsystemrK   rJ   r)   rQ   )r+   rP   rG   extendrS   rB   )r1   rQ   responses      r6   to_completionz _KdtSuggestPayload.to_completionj   s    (D4G4G4I4IJJJKKK))++,,,&$-2E2FFFGGG***r8   )r9   r:   r;   r<   rB   r	   r=   r>   r   r   rG   r   rS   rY   r?   r8   r6   rA   rA   N   s         ##"Hhsm"""$%%%%" " " " "d4j    t      r8   rA   c                       e Zd ZU dZeed<   dS )_KdtoSuggestRequestr   payloadN)r9   r:   r;   r<   rA   r>   r?   r8   r6   r[   r[   s   s$         ##r8   r[   c                   B    e Zd ZU dZ edd          Zeed<   eed<   dS )_KdtMessagepydantic API response typer)   zOne of [user|assistant|system]r   rL   rM   N)r9   r:   r;   r<   r   rL   r=   r>   r?   r8   r6   r^   r^   y   s?         $$b(HIIID#IIILLLLLr8   r^   c                   X    e Zd ZU dZeed<    edd          Zee	         ed<   e
ed<   dS )
_KdtChoicer_   indexNzThe generated SQLr   messagefinish_reason)r9   r:   r;   r<   intr>   r   rc   r	   r^   r=   r?   r8   r6   ra   ra      sR         $$JJJ%*U4?R%S%S%SGXk"SSSr8   ra   c                   2    e Zd ZU dZeed<   eed<   eed<   dS )	_KdtUsager_   prompt_tokenscompletion_tokenstotal_tokensN)r9   r:   r;   r<   re   r>   r?   r8   r6   rg   rg      s<         $$r8   rg   c                       e Zd ZU dZeed<   eed<   eed<   eed<   ee         ed<   e	ed<    e
dd	
          Zeed<   dS )_KdtSqlResponser_   idobjectcreatedmodelchoicesusager)   zThe input questionr   promptN)r9   r:   r;   r<   r=   r>   re   r   ra   rg   r   rs   r?   r8   r6   rl   rl      sy         $$GGGKKKLLLJJJ*%*>???FC?????r8   rl   c                   (    e Zd ZU dZeed<   eed<   dS )_KdtCompletionResponser_   statusdataN)r9   r:   r;   r<   r=   r>   rl   r?   r8   r6   ru   ru      s-         $$KKK
r8   ru   c                       e Zd ZU dZ ej        dej                  Zee	d<   e
dededefd            Ze
dej        defd	            Ze
ded
edefd            ZdS )_KineticaLlmFileContextParserz*Parser for Kinetica LLM context datafiles.z'^<\|(?P<role>\w+)\|>\W*(?P<content>.*)$PARSERtextsuffixr%   c                 d    |r-|                     |          r|d t          |                    S |S )N)endswithr,   )clsr{   r|   s      r6   _removesuffixz+_KineticaLlmFileContextParser._removesuffix   s9     	(dmmF++ 	(3v;;,''r8   
input_filec                     t          |          }|                     |j        d          }t          |                                          }|                     ||          S )Nz.txt)r   r   nameopenreadparse_dialogue)r   r   pathschemar2   s        r6   parse_dialogue_filez1_KineticaLlmFileContextParser.parse_dialogue_file   sU    J""49f55Z  %%''!!%000r8   r   c                    g }d }|                     d          }d }t          |          D ]\  }}|                                }t          |          dk    r.| j                            |          }	|	t          d|           |	                                }
|
d         }|dk    r|t          d|           |
d         }|dk    r|t          d	|           |
}|d
k    rA|t          d|           |	                    |           |	                    |
           d }t          d|           |||dS )Nz<|end|>r   z"Could not find starting token in: rL   rU   z"Only one system token allowed in: rM   rJ   z*Found user token without assistant token: rN   z*Found assistant token without user token: zUnknown token: )r   rU   rQ   )
split	enumerater/   r,   rz   matchr-   	groupdict	Exceptionr+   )r   r{   r   rQ   rU   r2   user_messageidxliner   r   rL   s               r6   r   z,_KineticaLlmFileContextParser.parse_dialogue   s   

9%%"5)) 	; 	;IC::<<D4yyA~~J$$T**E} !Ld!L!LMMM))IV$Dx%$%P$%P%PQQQ"9-+$KTKK    )$$'#$WQU$W$WXXX---	***# !94!9!9::: FIIIr8   N)r9   r:   r;   r<   recompileDOTALLrz   r
   r>   classmethodr=   r   osPathLiker   r   r   r?   r8   r6   ry   ry      s         44 !bj!KRYWWFGWWW c c    [
 1R[ 1T 1 1 1 [1 'J# 'Js 'Jt 'J 'J 'J ['J 'J 'Jr8   ry   c            
           e Zd ZdZe	 	 	 ddee         dee         dee         ddfd            Zed	ed
ee         defd            ZdS )KineticaUtilzKinetica utility functions.NurlrJ   passwdr%   zgpudb.GPUdbc                    	 ddl }n# t          $ r t          d          w xY w|                     d|          }|                     d|          }|                     d|          }|j                                        }||_        ||_        d|_        d|_	        d|_
         |j        ||	          }t                              d
                    |                                t          d          |j                             |S )a  Create a connectica connection object and verify connectivity.

        If None is passed for one or more of the parameters then an attempt will be made
        to retrieve the value from the related environment variable.

        Args:
            url: The Kinetica URL or ``KINETICA_URL`` if None.
            user: The Kinetica user or ``KINETICA_USER`` if None.
            passwd: The Kinetica password or ``KINETICA_PASSWD`` if None.

        Returns:
            The Kinetica connection object.
        r   NzUCould not import Kinetica python package. Please install it with `pip install gpudb`.KINETICA_URLKINETICA_USERKINETICA_PASSWDTINFO)hostoptionsz.Connected to Kinetica: {}. (api={}, server={})gpudb)r   ModuleNotFoundErrorImportError_get_envGPUdbOptionsusernamepasswordskip_ssl_cert_verificationdisable_failoverlogging_levelLOGinfoformatget_urlr   server_version)r   r   rJ   r   r   r   kdbcs          r6   create_kdbczKineticaUtil.create_kdbc   s   *	LLLL" 	 	 	>  	 ll>3//||OT22/88+%%''!-1*#'  &u{W555<CC 0 0$2E 	
 	
 	
 s    !r   r   c                 ^    ||S t          j        |          }||S t          d|           )z-Get an environment variable or use a default.Nz;Parameter was not passed and not found in the environment: )r   getenvr-   )r   r   r   r5   s       r6   r   zKineticaUtil._get_env  sD     N4MP$PP
 
 	
r8   )NNN)	r9   r:   r;   r<   r   r	   r=   r   r   r?   r8   r6   r   r      s        %% "" $	. .c]. sm. 	.
 
. . . [.` 
C 
(3- 
C 
 
 
 [
 
 
r8   r   c                      e Zd ZU dZ ed          Zeed<   	 ede	de	fd            Z
edefd            Zede	eef         fd	            Z	 	 ddee         deee                  dee         dedef
dZdedefdZdee	         defdZdede	fdZededee         fd            Zedede	fd            Zede	defd            Zede	dee         fd            Zd
S )ChatKineticaaK  Kinetica LLM Chat Model API.

    Prerequisites for using this API:

    * The ``gpudb`` and ``typeguard`` packages installed.
    * A Kinetica DB instance.
    * Kinetica host specified in ``KINETICA_URL``
    * Kinetica login specified ``KINETICA_USER``, and ``KINETICA_PASSWD``.
    * An LLM context that specifies the tables and samples to use for inferencing.

    This API is intended to interact with the Kinetica SqlAssist LLM that supports
    generation of SQL from natural language.

    In the Kinetica LLM workflow you create an LLM context in the database that provides
    information needed for infefencing that includes tables, annotations, rules, and
    samples. Invoking ``load_messages_from_context()`` will retrieve the contxt
    information from the database so that it can be used to create a chat prompt.

    The chat prompt consists of a ``SystemMessage`` and pairs of
    ``HumanMessage``/``AIMessage`` that contain the samples which are question/SQL
    pairs. You can append pairs samples to this list but it is not intended to
    facilitate a typical natural language conversation.

    When you create a chain from the chat prompt and execute it, the Kinetica LLM will
    generate SQL from the input. Optionally you can use ``KineticaSqlOutputParser`` to
    execute the SQL and return the result as a dataframe.

    The following example creates an LLM using the environment variables for the
    Kinetica connection. This will fail if the API is unable to connect to the database.

    Example:
        .. code-block:: python

            from langchain_community.chat_models.kinetica import KineticaChatLLM
            kinetica_llm = KineticaChatLLM()

    If you prefer to pass connection information directly then you can create a
    connection using ``KineticaUtil.create_kdbc()``.

    Example:
        .. code-block:: python

            from langchain_community.chat_models.kinetica import (
                KineticaChatLLM, KineticaUtil)
            kdbc = KineticaUtil._create_kdbc(url=url, user=user, passwd=passwd)
            kinetica_llm = KineticaChatLLM(kdbc=kdbc)
    Texcluder   valuesr%   c                 r    |                     dd          }|t                                          }||d<   |S )zPydantic object validator.r   N)getr   r   )r   r   r   s      r6   validate_environmentz!ChatKinetica.validate_environmentZ  s;     zz&$''<++--D!F6Nr8   c                     dS )Nzkinetica-sqlassistr?   r1   s    r6   	_llm_typezChatKinetica._llm_typed  s    ##r8   c                 l    t          t          | j        j                  t	          d                    S )Nr   )kinetica_versionapi_version)rP   r=   r   r   r   r   s    r6   _identifying_paramsz ChatKinetica._identifying_paramsh  s4     !9::PWHXHX
 
 
 	
r8   NrQ   stoprun_managerkwargsc                     |t          d           fd|D             }                     |          }t          t          |j        d         j                  }|                                }                     |          }	t          |j	        j
        |j	        j        |j                  }
t          t          |	          g|
          S )Nzstop kwargs are not permitted.c                 :    g | ]}                     |          S r?   )_convert_message_to_dict.0mr1   s     r6   
<listcomp>z*ChatKinetica._generate.<locals>.<listcomp>x  s'    LLLa66q99LLLr8   r   )input_tokensoutput_tokens
model_name)rc   )generations
llm_output)r-   _submit_completionr   r^   rq   rc   
model_dump_convert_message_from_dictrP   rr   rh   ri   rp   r   r   )r1   rQ   r   r   r   dict_messagessql_responseresponse_messagegenerated_dictgenerated_messager   s   `          r6   	_generatezChatKinetica._generaten  s     =>>>LLLL8LLL..}==\-A!-D-LMM)4466 ;;NKK%+9&,>#)
 
 


 '0ABBBC!
 
 
 	
r8   context_namec                     d| d}                      |          }|d         }t          j        |          }t                              |          }|j        }g }|                    t          d|                                                     |	                    |
                                            fd|D             }	|	S )aR  Load a lanchain prompt from a Kinetica context.

        A Kinetica Context is an object created with the Kinetica Workbench UI or with
        SQL syntax. This function will convert the data in the context to a list of
        messages that can be used as a prompt. The messages will contain a
        ``SystemMessage`` followed by pairs of ``HumanMessage``/``AIMessage`` that
        contain the samples.

        Args:
            context_name: The name of an LLM context in the database.

        Returns:
            A list of messages containing the information from the context.
        z/GENERATE PROMPT WITH OPTIONS (CONTEXT_NAMES = 'z')PromptrU   rK   c                 :    g | ]}                     |          S r?   r   r   s     r6   r   z;ChatKinetica.load_messages_from_context.<locals>.<listcomp>  s'    NNN1D33A66NNNr8   )_execute_sqljsonloadsr[   model_validater\   r+   rP   rG   rW   rS   )
r1   r   sqlr5   rs   prompt_jsonrequestr\   r   rQ   s
   `         r6   load_messages_from_contextz'ChatKinetica.load_messages_from_context  s    " QPPP""3''!j((
 &44[AA/Tx9O9O9Q9QRRRSSSW1133444NNNNNNNr8   c                 0   t          |          }t          j        |          }| j                            d|          }t          j        |          }|d         }|dk    rs|d         }t          j        d          }|                    |          }	|	1|		                    d          }
t          j        |
          }|d         }t          |          |d	         }t                              |          }
|
j        dk    rt          d
          |
j        S )z/Submit a /chat/completions request to Kinetica.rV   z/chat/completionsrv   OKrc   zresponse:({.*})N   rw   zSQL Generation failed)rP   r   dumpsr   _GPUdb__submit_request_jsonr   r   r   searchgroupr-   ru   r   rv   rw   )r1   rQ   r   request_jsonresponse_rawresponse_jsonrv   rc   
match_respr5   rX   rw   s               r6   r   zChatKinetica._submit_completion  s    )))z'**y<<
 
 
<00x(T>>#I.G$677J&&w//F!!<<?? $
8 4 4'	2W%%%V$)88>>?d""4555}r8   r   c                 <   | j                             |dd          }|d         }|d         dk    r|d         }t          |          |d         }t          |          dk    rt          d	          |d
         }i }|                                D ]
\  }}	|	||<   |S )z+Execute an SQL query and return the result.r   F)limitget_column_majorstatus_inforv   r   rc   recordszNo records returned.r   )r   execute_sql_and_decoder-   r,   rO   )
r1   r   rX   r   rc   r   recordresponse_dictcolvals
             r6   r   zChatKinetica._execute_sql  s     933q5 4 
 
 }-x D((!),GW%%%9%w<<13444 	% 	%HC!$M#r8   sa_datafilec                 d    t                               |          }|                     |          }|S )z8Load a lanchain prompt from a Kinetica context datafile.)ry   r   _convert_dict_to_messages)r   r  datafile_dictrQ   s       r6   load_messages_from_datafilez(ChatKinetica.load_messages_from_datafile  s/     6II+VV00??r8   rc   c                    t          t          |j                  }t          |t                    rd}nBt          |t
                    rd}n*t          |t                    rd}nt          d|           t          ||          }|S )z*Convert a single message to a BaseMessage.rJ   rN   rU   zGot unsupported message type: rK   )	r   r=   rM   
isinstancer   r   r   r-   rP   )r   rc   rM   rL   result_messages        r6   r   z%ChatKinetica._convert_message_to_dict  s     sGO,,g|,, 	IDD++ 	IDD// 	IDDGgGGHHH4999r8   c                     |d         }|d         }|dk    rt          |          S |dk    rt          |          S |dk    rt          |          S t          d|           )z,Convert a single message from a BaseMessage.rL   rM   rJ   rM   rN   rU   zGot unsupported role: )r   r   r   r-   )r   rc   rL   rM   s       r6   r   z'ChatKinetica._convert_message_from_dict  s     v)$6>>0000[  W----X 1111<d<<===r8   sa_datac                      |d         }|d         }|d         }t                               d|            g }|                    t          |                     |                     fd|D                        |S )z)Convert a dict to a list of BaseMessages.r   rU   rQ   zImporting prompt for schema: r
  c                 :    g | ]}                     |          S r?   r   )r   r   r   s     r6   r   z:ChatKinetica._convert_dict_to_messages.<locals>.<listcomp>  s'    PPP!C::1==PPPr8   )r   r   r+   r   rW   )r   r  r   rU   rQ   result_lists   `     r6   r  z&ChatKinetica._convert_dict_to_messages  s     "":&999:::)+=888999PPPPxPPPQQQr8   )NN)r9   r:   r;   r<   r   r   r   r>   r   r   r   propertyr=   r   r   r   r   r	   r   r   r   r   rl   r   r   r   r   r  r   r   r  r?   r8   r6   r   r   &  sW        . .` d###D#####$ 4    X $3 $ $ $ X$ 
T#s(^ 
 
 
 X
 %):>	
 
{#
 tCy!
 67	

 
 

 
 
 
8"s "t " " " "H4: /    8     , d tK?P    [ { t    [  > >+ > > > [>  k9J    [  r8   r   c                   t    e Zd ZU dZ ed          Zeed<   	  ed          Ze	ed<   	  e
d          ZdS )	KineticaSqlResponsezResponse containing SQL and the fetched data.

    This object is returned by a chain with ``KineticaSqlOutputParser`` and it contains
    the generated SQL and related Pandas Dataframe fetched from the database.
    r)   )r   r   N	dataframeTarbitrary_types_allowed)r9   r:   r;   r<   r   r   r=   r>   r  r   r   model_configr?   r8   r6   r  r    sw           uR   C    U4(((Is(((;: $  LLLr8   r  c                       e Zd ZU dZ ed          Zeed<   	  ed          Z	de
defdZd	d
dee         dedefdZede
fd            ZdS )KineticaSqlOutputParsera  Fetch and return data from the Kinetica LLM.

    This object is used as the last element of a chain to execute generated SQL and it
    will output a ``KineticaSqlResponse`` containing the SQL and a pandas dataframe with
    the fetched data.

    Example:
        .. code-block:: python

            from langchain_community.chat_models.kinetica import (
                KineticaChatLLM, KineticaSqlOutputParser)
            kinetica_llm = KineticaChatLLM()

            # create chain
            ctx_messages = kinetica_llm.load_messages_from_context(self.context_name)
            ctx_messages.append(("human", "{input}"))
            prompt_template = ChatPromptTemplate.from_messages(ctx_messages)
            chain = (
                prompt_template
                | kinetica_llm
                | KineticaSqlOutputParser(kdbc=kinetica_llm.kdbc)
            )
            sql_response: KineticaSqlResponse = chain.invoke(
                {"input": "What are the female users ordered by username?"}
            )

            assert isinstance(sql_response, KineticaSqlResponse)
            LOG.info(f"SQL Response: {sql_response.sql}")
            assert isinstance(sql_response.dataframe, pd.DataFrame)
    Tr   r   r  r{   r%   c                 X    | j                             |          }t          ||          S )N)r   r  )r   to_dfr  )r1   r{   dfs      r6   parsezKineticaSqlOutputParser.parseP  s(    Y__T"""tr::::r8   F)partialr5   r  c                B    |                      |d         j                  S )Nr   )r  r{   )r1   r5   r  s      r6   parse_resultz$KineticaSqlOutputParser.parse_resultT  s     zz&).)))r8   c                     dS )Nkinetica_sql_output_parserr?   r   s    r6   _typezKineticaSqlOutputParser._typeY  s    ++r8   N)r9   r:   r;   r<   r   r   r   r>   r   r  r=   r  r  r   r   boolr  r  r!  r?   r8   r6   r  r  )  s          > d###D#####: $  L;# ;"5 ; ; ; ;
 <A* * *:&*48*	* * * *
 ,s , , , X, , ,r8   r  )7r<   r   loggingr   r   importlib.metadatar   pathlibr   typingr   r   r   r   r	   r
   r   langchain_core.utilsr   r   langchain_core.callbacksr   *langchain_core.language_models.chat_modelsr   langchain_core.messagesr   r   r   r   'langchain_core.output_parsers.transformr   langchain_core.outputsr   r   r   pydanticr   r   r   	getLoggerr9   r   r   rA   r[   r^   ra   rg   rl   ru   ry   r   r   r  r  r?   r8   r6   <module>r/     s   ' &   				 				 & & & & & &       J J J J J J J J J J J J J J J J J J ) ) ) ) ) ) LLL = = = = = = D D D D D D            E D D D D D I I I I I I I I I I 1 1 1 1 1 1 1 1 1 1g!!
' ' ' ' ' ' ' 'T" " " " " " " "J         )          )              	   	@ 	@ 	@ 	@ 	@i 	@ 	@ 	@    Y   =J =J =J =J =J =J =J =J@@
 @
 @
 @
 @
 @
 @
 @
Fm m m m m= m m m`    )   &2, 2, 2, 2, 2,./BC 2, 2, 2, 2, 2,r8   