
    NgEh                        d dl m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mZmZ d dlmZ d dlmZ erd dlZ G d de          Z G d	 d
e          ZdS )    )annotationsN)md5)TYPE_CHECKINGAnyDictList
NamedTuplePatternTupleUnion)GraphDocument)
GraphStorec                  *    e Zd ZdZddZddZdd
ZdS )AGEQueryExceptionzException for the AGE queries.	exceptionUnion[str, Dict]returnNonec                    t          |t                    r(d|v r|d         nd| _        d|v r|d         nd| _        d S || _        d| _        d S )Nmessageunknowndetails)
isinstancedictr   r   )selfr   s     `/var/www/html/ai-engine/env/lib/python3.11/site-packages/langchain_community/graphs/age_graph.py__init__zAGEQueryException.__init__   s`    i&& 	%3<	3I3I9Y//yDL3<	3I3I9Y//yDLLL$DL$DLLL    strc                    | j         S N)r   r   s    r   get_messagezAGEQueryException.get_message   
    |r   r   c                    | j         S r!   )r   r"   s    r   get_detailszAGEQueryException.get_details   r$   r   N)r   r   r   r   r   r   )r   r   )__name__
__module____qualname____doc__r   r#   r&    r   r   r   r      sV        ((% % % %        r   r   c                  p   e Zd ZU dZdddddddZ ej        d	          Zd
ed<   	 d@dAdZ	dBdZ
dCdZdDdZdEdZedFd             ZdGd#ZdHd$ZdId%ZedJd&            ZedKd'            ZedLd+            ZedMd-            ZedNd0            Zi fdOd3Ze	 dPdQd8            ZedRd:            Z	 dSdTd?Zd4S )UAGEGrapha  
    Apache AGE wrapper for graph operations.

    Args:
        graph_name (str): the name of the graph to connect to or create
        conf (Dict[str, Any]): the pgsql connection config passed directly
            to psycopg2.connect
        create (bool): if True and graph doesn't exist, attempt to create it

    *Security note*: Make sure that the database connection uses credentials
        that are narrowly-scoped to only include necessary permissions.
        Failure to do so may result in data corruption or loss, since the calling
        code may attempt commands that would result in deletion, mutation
        of data if appropriately prompted or reading sensitive data if such
        data is present in the database.
        The best way to guard against such negative outcomes is to (as appropriate)
        limit the permissions granted to the credentials used with this tool.

        See https://python.langchain.com/docs/security for more information.
    STRINGDOUBLEINTEGERLISTMAPBOOLEAN)r   floatintlistr   boolz[^0-9a-zA-Z]+r
   label_regexT
graph_namer   confDict[str, Any]creater8   r   r   c                   || _         	 ddl}n# t          $ r t          d          w xY w |j        d	i || _        |                                 5 }d                    |          }|                    |           |                                }||rvd                    |          }	 |                    |           | j        	                                 nS# |j
        $ r$}	t          dt          |	          d          d}	~	ww xY wt          d                    |                    |                    |           |                                }|j        | _        |                                  ddd           dS # 1 swxY w Y   dS )
zCreate a new AGEGraph instance.r   NzXCould not import psycopg2 python package. Please install it with `pip install psycopg2`.z9SELECT graphid FROM ag_catalog.ag_graph WHERE name = '{}'zS
                        SELECT ag_catalog.create_graph('{}');
                    zCould not create the graphr   detailzFGraph "{}" does not exist in the database and "create" is set to Falser,   )r:   psycopg2ImportErrorconnect
connection_get_cursorformatexecutefetchonecommitErrorr   r   	Exceptiongraphidrefresh_schema)
r   r:   r;   r=   rA   cursgraph_id_querydatacreate_statementes
             r   r   zAGEGraph.__init__D   s   
 %	OOOO 	 	 	A  	 +(*22T22 *	"4 PVV   LL(((==??D | (z** %	%5666..0000#>   /+G*-a&&    $= &,,	   ^,,,}}  <DL!!!U*	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	" *	"s@    (AF*.CF
D#DDA.FF	F psycopg2.extras.NamedTupleCursorc                    	 ddl }n"# t          $ r}t          d          |d}~ww xY w| j                            |j        j                  }|                    d           |                    d           |S )zD
        get cursor, load age extension and set search path
        r   NIUnable to import psycopg2, please install with `pip install -U psycopg2`.)cursor_factoryzLOAD 'age';z.SET search_path = ag_catalog, "$user", public;)psycopg2.extrasrB   rD   cursorextrasNamedTupleCursorrG   )r   rA   rR   rX   s       r   rE   zAGEGraph._get_cursor   s    
	""""" 	 	 	-  	
 ''x7W'XX()))KLLLs    
&!&Tuple[List[str], List[str]]c                    |                      d          }|r|d         d         ng }|                      d          }|r|d         d         ng }||fS )a  
        Get all labels of a graph (for both edges and vertices)
        by querying the graph metadata table directly

        Returns
            Tuple[List[str]]: 2 lists, the first containing vertex
                labels and the second containing edge labels
        z;MATCH ()-[e]-() RETURN collect(distinct label(e)) as labelsr   labelsz5MATCH (n) RETURN collect(distinct label(n)) as labels)query)r   e_labels_recordse_labelsn_labels_recordsn_labelss        r   _get_labelszAGEGraph._get_labels   sw      ::M
 
 5EL#A&x00"::G
 
 5EL#A&x00"!!r   r`   	List[str]List[Dict[str, str]]c           
        	 ddl }n"# t          $ r}t          d          |d}~ww xY wd}g }|                                 5 }|D ]}|                    | j        |          }	 |                    |           |                                }	|	D ]l}
|                    t          j	        |
j
                  d         t          j	        |
j                  t          j	        |
j                  d         d           m# |j        $ r$}t          dt          |          d          d}~ww xY w	 ddd           n# 1 swxY w Y   |S )	a  
        Get a set of distinct relationship types (as a list of dicts) in the graph
        to be used as context by an llm.

        Args:
            e_labels (List[str]): a list of edge labels to filter for

        Returns:
            List[Dict[str, str]]: relationships as a list of dicts in the format
                "{'start':<from_label>, 'type':<edge_label>, 'end':<from_label>}"
        r   NrU   a(  
        SELECT * FROM ag_catalog.cypher('{graph_name}', $$
            MATCH (a)-[e:`{e_label}`]->(b)
            WITH a,e,b LIMIT 3000
            RETURN DISTINCT labels(a) AS from, type(e) AS edge, labels(b) AS to
            LIMIT 10
        $$) AS (f agtype, edge agtype, t agtype);
        r:   e_label)starttypeendzError fetching triplesr?   )rA   rB   rE   rF   r:   rG   fetchallappendjsonloadsfedgetrJ   r   r   )r   r`   rA   rR   triple_querytriple_schemarN   labelqrP   ds              r   _get_tripleszAGEGraph._get_triples   s   	OOOO 	 	 	-  	
   	4!   ''4?E'RRLLOOO==??D! 	 	 &,,)-AC);(,
16(:(:'+z!#q'9    	  ~   +'?&)!ff   	 	 	 	 	 	 	 	 	 	 	 	 	 	 	0 sG    
&!&!D:#BC<;D:<
D*D%%D**D::D>D>c                V    |                      |          }|                     |          S )a}  
        Get a set of distinct relationship types (as a list of strings) in the graph
        to be used as context by an llm.

        Args:
            e_labels (List[str]): a list of edge labels to filter for

        Returns:
            List[str]: relationships as a list of strings in the format
                "(:`<from_label>`)-[:`<edge_label>`]->(:`<to_label>`)"
        )rx   _format_triples)r   r`   tripless      r   _get_triples_strzAGEGraph._get_triples_str   s+     ##H--##G,,,r   r{   c                (    dfd| D             }|S )a  
        Convert a list of relationships from dictionaries to formatted strings
        to be better readable by an llm

        Args:
            triples (List[Dict[str,str]]): a list relationships in the form
                {'start':<from_label>, 'type':<edge_label>, 'end':<from_label>}

        Returns:
            List[str]: a list of relationships in the form
                "(:`<from_label>`)-[:`<edge_label>`]->(:`<to_label>`)"
        z$(:`{start}`)-[:`{type}`]->(:`{end}`)c                *    g | ]} j         d i |S )r,   )rF   ).0tripletriple_templates     r   
<listcomp>z,AGEGraph._format_triples.<locals>.<listcomp>  s-    PPPf//99&99PPPr   r,   )r{   rt   r   s     @r   rz   zAGEGraph._format_triples   s*     APPPPPPPr   rb   List[Dict[str, Any]]c                   	 ddl }n"# t          $ r}t          d          |d}~ww xY wd}g }|                                 5 }|D ]}|                    | j        |          }	 |                    |           n1# |j        $ r$}t          dt          |          d          d}~ww xY w|	                                }	t          i           }
|	D ]g}t          j        |j                                                  D ]9\  }}|
                    || j        t#          |          j                 f           :hd |
D             |d	}|                    |           	 ddd           n# 1 swxY w Y   |S )
a  
        Fetch a list of available node properties by node label to be used
        as context for an llm

        Args:
            n_labels (List[str]): a list of node labels to filter for

        Returns:
            List[Dict[str, Any]]: a list of node labels and
                their corresponding properties in the form
                "{
                    'labels': <node_label>,
                    'properties': [
                        {
                            'property': <property_name>,
                            'type': <property_type>
                        },...
                        ]
                }"
        r   NrU   z
        SELECT * FROM ag_catalog.cypher('{graph_name}', $$
            MATCH (a:`{n_label}`)
            RETURN properties(a) AS props
            LIMIT 100
        $$) AS (props agtype);
        )r:   n_labelzError fetching node propertiesr?   c                    g | ]
\  }}||d S )propertyrj   r,   r   kvs      r   r   z1AGEGraph._get_node_properties.<locals>.<listcomp>I  $    "L"L"L$!Q1#=#="L"L"Lr   )
propertiesr]   rA   rB   rE   rF   r:   rG   rJ   r   r   rl   setrn   ro   propsitemsaddtypesrj   r(   rm   )r   rb   rA   rR   node_properties_querynode_propertiesrN   ru   rv   rP   srw   r   r   nps                  r   _get_node_propertieszAGEGraph._get_node_properties  s$   *	OOOO 	 	 	-  	!  	+4! + +)00# 1  LLOOOO~   +'G&)!ff    }} GG A AA !%
17 3 3 9 9 ; ; A A1q$*T!WW-=">?@@@@A #M"L!"L"L"L#   &&r****9+	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+> G    
&!&"E+$A:9E+:
B(B##B((B6E++E/2E/c                   	 ddl }n"# t          $ r}t          d          |d}~ww xY wd}g }|                                 5 }|D ]}|                    | j        |          }	 |                    |           n1# |j        $ r$}t          dt          |          d          d}~ww xY w|	                                }	t          i           }
|	D ]g}t          j        |j                                                  D ]9\  }}|
                    || j        t#          |          j                 f           :hd |
D             |d	}|                    |           	 ddd           n# 1 swxY w Y   |S )
a  
        Fetch a list of available edge properties by edge label to be used
        as context for an llm

        Args:
            e_labels (List[str]): a list of edge labels to filter for

        Returns:
            List[Dict[str, Any]]: a list of edge labels
                and their corresponding properties in the form
                "{
                    'labels': <edge_label>,
                    'properties': [
                        {
                            'property': <property_name>,
                            'type': <property_type>
                        },...
                        ]
                }"
        r   NrU   z
        SELECT * FROM ag_catalog.cypher('{graph_name}', $$
            MATCH ()-[e:`{e_label}`]->()
            RETURN properties(e) AS props
            LIMIT 100
        $$) AS (props agtype);
        rg   zError fetching edge propertiesr?   c                    g | ]
\  }}||d S r   r,   r   s      r   r   z1AGEGraph._get_edge_properties.<locals>.<listcomp>  r   r   )r   rj   r   )r   r`   rA   rR   edge_properties_queryedge_propertiesrN   ru   rv   rP   r   rw   r   r   r   s                  r   _get_edge_propertieszAGEGraph._get_edge_propertiesP  s$   ,	OOOO 	 	 	-  	!  	+4! + +)00# 1  LLOOOO~   +'G&)!ff    }} GG A AA !%
17 3 3 9 9 ; ; A A1q$*T!WW-=">?@@@@A #M"L!"L"L"L!   &&r****9+	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+> r   c                :   |                                  \  }}|                     |          }|                     |          }|                     |          }d| d| d|                     |           d| _        d |D             d |D             |i d| _        dS )	z~
        Refresh the graph schema information by updating the available
        labels, relationships, and properties
        z4
        Node properties are the following:
        z<
        Relationship properties are the following:
        z6
        The relationships are the following:
        z	
        c                ,    i | ]}|d          |d         S )r]   r   r,   r   els     r   
<dictcomp>z+AGEGraph.refresh_schema.<locals>.<dictcomp>  s#    TTTb2h<L)9TTTr   c                ,    i | ]}|d          |d         S )rj   r   r,   r   s     r   r   z+AGEGraph.refresh_schema.<locals>.<dictcomp>  s#    QQQ2"V*b&6QQQr   )
node_props	rel_propsrelationshipsmetadataN)rc   rx   r   r   rz   schemastructured_schema)r   rb   r`   rt   r   r   s         r   rM   zAGEGraph.refresh_schema  s     "--//())(3333H==33H==	  
	  
		m	,	,   UTOTTTQQQQQ*	"
 "
r   c                    | j         S )zReturns the schema of the Graph)r   r"   s    r   
get_schemazAGEGraph.get_schema  s     {r   c                    | j         S )z*Returns the structured schema of the Graph)r   r"   s    r   get_structured_schemazAGEGraph.get_structured_schema  s     %%r   fieldidxr6   c                   |                                  } d| v r-|                     d          d                                          S |                                 s| dv rd| S |                     dd                              dd          S )	a  
        Convert a cypher return field to a pgsql select field
        If possible keep the cypher column name, but create a generic name if necessary

        Args:
            field (str): a return field from a cypher query to be formatted for pgsql
            idx (int): the position of the field in the return statement

        Returns:
            str: the field to be used in the pgsql select statement
        z as )truefalsenullcolumn_(_) )stripsplit	isnumericreplace)r   r   s     r   _get_col_namezAGEGraph._get_col_name  s     U??;;v&&r*00222__ 	<%+D"D"D"S??" ==c**223;;;r   r^   c                `   d}d|                                  v r|                                                      d          d                             d          d                             d          d                             d          d                             d          d                             d	          }d
d |D             v rt          d          d t          |          D             }d                    d |D                       }nd}d
}|                    || ||          S )a  
        Convert a cypher query to an Apache Age compatible
        sql query by wrapping the cypher query in ag_catalog.cypher,
        casting results to agtype and building a select statement

        Args:
            query (str): a valid cypher query
            graph_name (str): the name of the graph to query

        Returns:
            str: an equivalent pgsql query
        zlSELECT {projection} FROM ag_catalog.cypher('{graph_name}', $$
            {query}
        $$) AS ({fields});r   r   distinctzorder byr   skiplimit,*c                6    g | ]}|                                 S r,   )r   )r   xs     r   r   z(AGEGraph._wrap_query.<locals>.<listcomp>  s     111Qqwwyy111r   zBAGE graph does not support 'RETURN *' statements in Cypher queriesc                J    g | ] \  }}t                               ||          !S r,   )r.   r   )r   r   r   s      r   r   z(AGEGraph._wrap_query.<locals>.<listcomp>  s9       7AsE&&uc22  r   , c                J    g | ] }|                     d           d         dz   !S ).r   z agtype)r   )r   r   s     r   r   z(AGEGraph._wrap_query.<locals>.<listcomp>  s.    FFFeS!!"%	1FFFr   za agtype)r:   r^   fields
projection)lowerr   
ValueError	enumeratejoinrF   )r^   r:   templater   
fields_str
select_strs         r   _wrap_queryzAGEGraph._wrap_query  sU    
 u{{}}$$ x%z""2' z""1& vq	"
 w# s  11&11111 6   ENvEVEV  F
 FFvFFF JJ $J
!!	  
 
 	
r   recordr	   c                   i }i }| j         D ]}t          | |          }t          |t                    rrd|v rn|                    d          d         }|                    d          d         }|dk    r2t          j        |          }|                    d          ||d         <   | j         D ]!}t          | |          }t          |t                    r;d|v r7|                    d          d         }|                    d          d         }nd}|dk    r+t          j        |                              d          ||<   |dk    rWt          j        |          }|                    |d	         i           |d
         |                    |d         i           f||<   t          |t                    rt          j        |          n|||<   #|S )an  
        Convert a record returned from an age query to a dictionary

        Args:
            record (): a record from an age query result

        Returns:
            Dict[str, Any]: a dictionary representation of the record where
                the dictionary key is the field name and the value is the
                value converted to a python type
        z::r   r   vertexr   idr   rq   start_idru   end_id)_fieldsgetattrr   r   r   rn   ro   get)r   rw   verticesr   r   dtyper   rq   s           r   _record_to_dictzAGEGraph._record_to_dict  s      	F 	FA""A!S!! Fdaiib)GGDMM!$H$$!Z]]F-3ZZ-E-EHVD\*  	B 	BA""A!S!! daiib)GGDMM!$  z!}}((66! &z!}}LLj!1266MLLh44! )31c(:(:Atz!}}}!r   paramsr   c                R    	 ddl }n"# t          $ r}t          d          |d}~ww xY w                     | j                  }                                 5 }	 |                    |            j                                         n]# |j        $ rP} j        	                                 t          d                    |          t          |          d          d}~ww xY w|                                }|g }n fd|D             }|cddd           S # 1 swxY w Y   dS )a  
        Query the graph by taking a cypher query, converting it to an
        age compatible query, executing it and converting the result

        Args:
            query (str): a cypher query to be executed
            params (dict): parameters for the query (not used in this implementation)

        Returns:
            List[Dict[str, Any]]: a list of dictionaries containing the result set
        r   NrU   zError executing graph query: {}r?   c                :    g | ]}                     |          S r,   )r   )r   rw   r   s     r   r   z"AGEGraph.query.<locals>.<listcomp>y  s'    @@@a$..q11@@@r   )rA   rB   r   r:   rE   rG   rD   rI   rJ   rollbackr   rF   r   rl   )	r   r^   r   rA   rR   wrapped_queryrN   rP   results	   `        r   r^   zAGEGraph.queryO  s   	OOOO 	 	 	-  	 ((@@  	4
]+++&&((((>   ((***'#D#K#KE#R#R"%a&&    ==??D| A@@@4@@@)	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	sG    
'"'D.B
	D

C$ACC$$+DD #D Nr   r   Union[str, None]c                b   g }|                                  D ]4\  }}d| dt          j        |           }|                    |           5|Hd| vrD|                    t	          |t
                    rdt          j        |           nd|            dd                    |          z   dz   S )	a  
        Convert a dictionary of properties to a string representation that
        can be used in a cypher query insert/merge statement.

        Args:
            properties (Dict[str,str]): a dictionary containing node/edge properties
            id (Union[str, None]): the id of the node or None if none exists

        Returns:
            str: the properties dictionary as a properly formatted string
        `z`: Nr   zid: {r   })r   rn   dumpsrm   r   r   r   )r   r   r   r   r   props         r   _format_propertieszAGEGraph._format_properties}  s     $$&& 	 	DAq,q,,TZ]],,DLL>d*44LL+5b#+>+>O'tz"~~'''K2KK   TYYu%%%++r   ru   c                B    t          j        t          j        d|           S )z
        remove any disallowed characters from a label and replace with '_'

        Args:
            label (str): the original label

        Returns:
            str: the sanitized version of the label
        r   )resubr.   r9   )ru   s    r   clean_graph_labelszAGEGraph.clean_graph_labels  s     vh*C777r   Fgraph_documentsList[GraphDocument]include_sourcec           
        |sdnd}d}|D ]l}|rj|j         j                            d          sKt          |j         j                            d                                                    |j         j        d<   |j        D ]}|j        |j	        d<   |rS|
                    |j        |                     |j	                  |                     |j         j                            }nL|
                    t                              |j                  |                     |j	                            }|                     |           |j        D ]%}|j         j        |j         j	        d<   |j        j        |j        j	        d<   t                              |j         j                  |                     |j         j	                  t                              |j        j                  |                     |j        j	                  t                              |j                                                  |                     |j	                  d}	 |j
        d
i |	}|                     |           'nd	S )aQ  
        insert a list of graph documents into the graph

        Args:
            graph_documents (List[GraphDocument]): the list of documents to be inserted
            include_source (bool): if True add nodes for the sources
                with MENTIONS edges to the entities they mention

        Returns:
            None
        z6
            MERGE (n:`{label}` {properties})
        z
            MERGE (n:`{label}` {properties})
            MERGE (d:Document {d_properties})
            MERGE (d)-[:MENTIONS]->(n)
        z
            MERGE (from:`{f_label}` {f_properties})
            MERGE (to:`{t_label}` {t_properties})
            MERGE (from)-[:`{r_label}` {r_properties}]->(to)
        r   zutf-8)ru   r   d_properties)ru   r   )f_labelf_propertiest_labelt_propertiesr_labelr_propertiesNr,   )sourcer   r   r   page_contentencode	hexdigestnodesr   r   rF   rj   r   r.   r   r^   r   targetupper)
r   r   r   node_insert_queryedge_insert_querydocnoder^   rq   inputss
             r   add_graph_documentszAGEGraph.add_graph_documents  sr   & "   	 # '	" '	"C "z*..t44 "03
/66w??1 1ikk J'-
 	 " "(,%! 
-44"i#'#:#:4?#K#K%)%<%<SZ=P%Q%Q 5  EE .44&99$)DD#'#:#:4?#K#K 5  E
 

5!!!! ) " "/3{~&t,/3{~&t,'::4;;KLL$($;$;DK<R$S$S'::4;;KLL$($;$;DK<R$S$S'::49EEKKMM$($;$;DO$L$L  1)0::6::

5!!!!"5'	" '	"r   )T)r:   r   r;   r<   r=   r8   r   r   )r   rS   )r   r[   )r`   rd   r   re   )r`   rd   r   rd   )r{   re   r   rd   )rb   rd   r   r   )r`   rd   r   r   )r   r   r'   )r   r<   )r   r   r   r6   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   )ru   r   r   r   )F)r   r   r   r8   r   r   )r(   r)   r*   r+   r   r   compiler9   __annotations__r   rE   rc   rx   r|   staticmethodrz   r   r   rM   r   r   r   r   r   r   r^   r   r   r  r,   r   r   r.   r.   !   sg         .  E &2:o66K6666 EI<" <" <" <" <"|   "" " " ",9 9 9 9v- - - -"    \$F F F FPE E E EN
 
 
 
>    X & & & X& < < < \<0 <
 <
 <
 \<
| 4 4 4 \4l 02 , , , , ,\ ;?, , , , \,2 
8 
8 
8 \
8 LQI" I" I" I" I" I" I"r   r.   )
__future__r   rn   r   hashlibr   typingr   r   r   r   r	   r
   r   r   )langchain_community.graphs.graph_documentr   &langchain_community.graphs.graph_storer   rW   rA   rK   r   r.   r,   r   r   <module>r     s   " " " " " "  				       T T T T T T T T T T T T T T T T T T T T C C C C C C = = = = = =     	   $L" L" L" L" L"z L" L" L" L" L"r   