
    dhsk                        S SK Jr  S SKrS SKrS SKJr  S SKJrJrJ	r	J
r
JrJrJrJr  S SKJr  S SKJr  \(       a  S SKr " S S\5      r " S	 S
\5      rg)    )annotationsN)md5)TYPE_CHECKINGAnyDictList
NamedTuplePatternTupleUnion)GraphDocument)
GraphStorec                  6    \ rS rSrSrSS jrS	S jrS
S jrSrg)AGEQueryException   zException for the AGE queries.c                    [        U[        5      (       a%  SU;   a  US   OSU l        SU;   a  US   OSU l        g Xl        SU l        g )Nmessageunknowndetails)
isinstancedictr   r   )self	exceptions     \/var/www/html/shao/venv/lib/python3.13/site-packages/langchain_community/graphs/age_graph.py__init__AGEQueryException.__init__   sE    i&&3<	3I9Y/yDL3<	3I9Y/yDL$L$DL    c                    U R                   $ N)r   r   s    r   get_messageAGEQueryException.get_message       ||r   c                    U R                   $ r   )r   r    s    r   get_detailsAGEQueryException.get_details   r#   r   )r   r   N)r   zUnion[str, Dict]returnNoner'   str)r'   r   )	__name__
__module____qualname____firstlineno____doc__r   r!   r%   __static_attributes__ r   r   r   r      s    (%r   r   c                     \ rS rSr% SrSSSSSSS	.r\R                  " S
5      rS\	S'    S!       S"S jjr
S#S jrS$S jrS%S jrS&S jr\S'S j5       rS(S jrS)S jrS*S jr\S+S j5       r\S,S j5       r\S-S j5       r\S.S j5       r\S/S j5       r0 4S0S jjr\ S1     S2S jj5       r\S3S j5       r S4     S5S jjrS rg)6AGEGraph!   a  
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_regexc                   Xl          SSKnUR                  " S	0 UD6U l        U R                  5        nSR                  U5      nUR                  U5        UR                  5       nUc  U(       a>  SR                  U5      n UR                  U5        U R                  R                  5         O[        SR                  U5      5      eUR                  U5        UR                  5       nUR                  U l        U R                  5         SSS5        g! [         a    [        S5      ef = f! UR                   a  n	[        S[        U	5      S.5      eSn	A	ff = f! , (       d  f       g= f)
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 Falser1   )
graph_namepsycopg2ImportErrorconnect
connection_get_cursorformatexecutefetchonecommitErrorr   r*   	Exceptiongraphidrefresh_schema)
r   rC   confcreaterD   cursgraph_id_querydatacreate_statementes
             r   r   AGEGraph.__init__D   s_   
 %	 #**2T24 PVV  LL(==?D |(z* %	%56..0 $= &,	  ^,}}  <<DL!U    	A 	: $>> /+G*-a& +  s<   D AE+D,-AED),E<EEE
E)c                     SSK nU R                  R                  UR                  R
                  S9nUR                  S5        UR                  S5        U$ ! [         a  n[        S5      UeSnAff = f)z4
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.extrasrE   rG   cursorextrasNamedTupleCursorrJ   )r   rD   rW   r]   s       r   rH   AGEGraph._get_cursor   su    
	" ''x7W7W'X()KL  	- 	s   A 
A2!A--A2c                    U R                  S5      nU(       a  US   S   O/ nU R                  S5      nU(       a  US   S   O/ nXB4$ )z
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_labelsAGEGraph._get_labels   s_      ::M
 5E#A&x0"::G
 5E#A&x0"!!r   c           
         SSK nSn/ nU R                  5        nU H  nUR                  U R                  US9n UR                  U5        UR                  5       n	U	 Hx  n
UR                  [        R                  " U
R                  5      S   [        R                  " U
R                  5      [        R                  " U
R                  5      S   S.5        Mz     M     SSS5        U$ ! [         a  n[        S5      UeSnAff = f! UR                   a  n[        S[        U5      S.5      eSnAff = f! , (       d  f       U$ = f)	aO  
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   NrZ   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);
        rC   e_label)starttypeendzError fetching triplesrA   )rD   rE   rH   rI   rC   rJ   fetchallappendjsonloadsfedgetrM   r   r*   )r   re   rD   rW   triple_querytriple_schemarS   labelqrU   ds              r   _get_triplesAGEGraph._get_triples   s7   	  4! ''4??E'RLLO==?D! &,,)-ACC);(,

166(:'+zz!##q'9 " "  0 S  	- 	B  ~~ +'?&)!f !  0 sF   C(  D5BDD5(
D2C>>DD2D--D22D55
Ec                F    U R                  U5      nU R                  U5      $ )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>`)"
)r|   _format_triples)r   re   tripless      r   _get_triples_strAGEGraph._get_triples_str   s%     ##H-##G,,r   c                T    SnU  Vs/ sH  o!R                   " S0 UD6PM     nnU$ s  snf )aw  
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}`)r1   )rI   )r   triple_templatetriplerx   s       r   r   AGEGraph._format_triples   s6     AHOPf//9&9P Qs   %c                    SSK nSn/ nU R                  5        nU H  nUR                  U R                  US9n UR                  U5        UR                  5       n	[        0 5      n
U	 Hl  n[        R                  " UR                  5      R                  5        H7  u  pU
R                  XR                   [#        U5      R$                     45        M9     Mn     U
 VVs/ sH	  u  pXS.PM     snnUS	.nUR'                  U5        M     SSS5        U$ ! [         a  n[        S5      UeSnAff = f! UR                   a  n[        S[        U5      S.5      eSnAff = fs  snnf ! , (       d  f       U$ = f)
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   NrZ   z
        SELECT * FROM ag_catalog.cypher('{graph_name}', $$
            MATCH (a:`{n_label}`)
            RETURN properties(a) AS props
            LIMIT 100
        $$) AS (props agtype);
        )rC   n_labelzError fetching node propertiesrA   propertyrn   )
propertiesrb   rD   rE   rH   rI   rC   rJ   rM   r   r*   rp   setrr   rs   propsitemsaddtypesrn   r+   rq   )r   rg   rD   rW   node_properties_querynode_propertiesrS   ry   rz   rU   sr{   kvnps                  r   _get_node_propertiesAGEGraph._get_node_properties  sz   *	! 4!)00# 1 LLO }} GA !%

177 3 9 9 ;q**T!W-=-=">?@ !<  KL"L!$!#=!"L#  &&r*9 "  > _  	- 	0  ~~ +'G&)!f $ #M5  > R   D  E&D1BE&E 
-E&
D.D))D.1EEE	E&&
E5c                    SSK nSn/ nU R                  5        nU H  nUR                  U R                  US9n UR                  U5        UR                  5       n	[        0 5      n
U	 Hl  n[        R                  " UR                  5      R                  5        H7  u  pU
R                  XR                   [#        U5      R$                     45        M9     Mn     U
 VVs/ sH	  u  pXS.PM     snnUS	.nUR'                  U5        M     SSS5        U$ ! [         a  n[        S5      UeSnAff = f! UR                   a  n[        S[        U5      S.5      eSnAff = fs  snnf ! , (       d  f       U$ = f)
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   NrZ   z
        SELECT * FROM ag_catalog.cypher('{graph_name}', $$
            MATCH ()-[e:`{e_label}`]->()
            RETURN properties(e) AS props
            LIMIT 100
        $$) AS (props agtype);
        rk   zError fetching edge propertiesrA   r   )r   rn   r   )r   re   rD   rW   edge_properties_queryedge_propertiesrS   ry   rz   rU   r   r{   r   r   r   s                  r   _get_edge_propertiesAGEGraph._get_edge_propertiesP  sz   ,	! 4!)00# 1 LLO }} GA !%

177 3 9 9 ;q**T!W-=-=">?@ !<  KL"L!$!#=!"L!  &&r*9 "  > [  	- 	,  ~~ +'G&)!f $ #M5  > r   c                R   U R                  5       u  pU R                  U5      nU R                  U5      nU R                  U5      nSU SU SU R	                  U5       S3U l        U Vs0 sH  ofS   US   _M     snU Vs0 sH  ofS   US   _M     snU0 S.U l        g	s  snf s  snf )
zf
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	
        rb   r   rn   )
node_props	rel_propsrelationshipsmetadataN)rh   r|   r   r   r   schemastructured_schema)r   rg   re   rx   r   r   els          r   rP   AGEGraph.refresh_schema  s     "--/))(333H=33H=		 		 				m	,- .	 ETTObh<L)99OTAPQ2V*b&66Q*	"
TQs   *BB$c                    U R                   $ )zReturns the schema of the Graph)r   r    s    r   
get_schemaAGEGraph.get_schema  s     {{r   c                    U R                   $ )z*Returns the structured schema of the Graph)r   r    s    r   get_structured_schemaAGEGraph.get_structured_schema  s     %%%r   c                    U R                  5       n SU ;   a"  U R                  S5      S   R                  5       $ U R                  5       (       d  U S;   a  SU 3$ U R                  SS5      R                  SS5      $ )	ac  
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)fieldidxs     r   _get_col_nameAGEGraph._get_col_name  st     U?;;v&r*0022__%+D"DSE?" ==c*223;;r   c                   U R                  5       (       d  [        S5      eSn[        R                  " SU [        R                  S9n/ nU GHW  nUR                  5       R                  5       S;   a  M(  [        R                  " SU[        R                  5      nU(       d  MW  XVR                  5       S nUR                  5       R                  S5      S	   R                  S
5      S   R                  S5      S   R                  S5      S   R                  S5      nU V	s/ sH)  oR                  5       (       d  M  U	R                  5       PM+     n
n	SU
;   a  [        S5      e[        U
5       H2  u  p[        R                  X5      nX;  d  M!  UR                  U5        M4     GMZ     U(       d  SnOSR                  S U 5       5      nUR                  UU USS9$ s  sn	f )a  
Convert a Cyper query to an Apache Age compatible Sql Query.
Handles combined queries with UNION/EXCEPT operators

Args:
    query (str) : A valid cypher query, can include UNION/EXCEPT operators
    graph_name (str) : The name of the graph to query

Returns :
    str : An equivalent pgSql query wrapped with ag_catalog.cypher

Raises:
    ValueError : If query is empty, contain RETURN *, or has invalid field names
zEmpty query providedzlSELECT {projection} FROM ag_catalog.cypher('{graph_name}', $$
            {query}
        $$) AS ({fields});z\b(UNION\b|\bEXCEPT)\b)flags)UNIONEXCEPTz\breturn\b(?![^"]*")Ndistinctr   zorder byr   skiplimit,*z6Apache Age does not support RETURN * in Cypher queriesza agtype, c              3  (   #    U H	  o S 3v   M     g7f)z agtypeNr1   ).0r   s     r   	<genexpr>'AGEGraph._wrap_query.<locals>.<genexpr>  s     "M*WG#4*s   )rC   rc   fields
projection)r   
ValueErrorrer   
IGNORECASEuppersearchro   lower	enumerater3   r   rq   joinrI   )rc   rC   templateparts
all_fieldspartreturn_matchreturn_clauser   rt   clean_filedsr   r   
field_name
fields_strs                  r   _wrap_queryAGEGraph._wrap_query  s   " {{}}344
 2EO
Dzz|!!#':: 99%<dBMMRL| $%5%5%7%9 : "'')U:&r+U:&q* U6]1& U7^A	'
 U3Z  4:G6aWWY		6G,&$P 
 #,L"9JC!)!7!7!CJ!3"))*5 #:9 D #J "M*"MMJ!	  
 	
'  Hs   G:Gc                d   0 n0 nU R                    H  n[        X5      n[        U[        5      (       d  M%  SU;   d  M-  UR	                  S5      S   nUR	                  S5      S   nUS:X  d  M]  [
        R                  " U5      nUR                  S5      X&S   '   M     U R                    GH  n[        X5      n[        U[        5      (       a/  SU;   a)  UR	                  S5      S   nUR	                  S5      S   nOSnUS:X  a)  [
        R                  " U5      R                  S5      X'   M  US:X  aH  [
        R                  " U5      nUR                  US	   0 5      US
   UR                  US   0 5      4X'   M  [        U[        5      (       a  [
        R                  " U5      OUX'   GM     U$ )a.  
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   ru   start_idry   end_id)_fieldsgetattrr   r*   r   rr   rs   get)recordr{   verticesr   r   dtyper   ru   s           r   _record_to_dictAGEGraph._record_to_dict&  sr     A"A!S!!daib)GGDM!$H$!ZZ]F-3ZZ-EHD\*   A"A!S!!daib)GGDM!$ zz!}((6 &zz!}LLj!126MLLh4 )31c(:(:tzz!}+  . r   c                T    SSK nU R                  XR                  5      nU R	                  5        n UR                  U5        U R                  R                  5         UR                  5       nUc  / nOU V	s/ sH  oR                  U	5      PM     nn	UsSSS5        $ ! [         a  n[        S5      UeSnAff = f! UR                   aE  nU R                  R                  5         [        SR                  U5      [        U5      S.5      eSnAff = fs  sn	f ! , (       d  f       g= f)aU  
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   NrZ   zError executing graph query: {}rA   )rD   rE   r   rC   rH   rJ   rG   rL   rM   rollbackr   rI   r*   rp   r   )
r   rc   paramsrD   rW   wrapped_queryrS   rU   resultr{   s
             r   rc   AGEGraph.query]  s   	 ((@ 4
]+&&( ==?D| <@@4a..q14@)    	- 	 >> ((*'#D#K#KE#R"%a&  A%  sR   B D+B<D8DD
B9(B44B9<DA DDD
D'Nc                V   / nU R                  5        H2  u  p4SU S[        R                  " U5       3nUR                  U5        M4     UbH  SU ;  aB  UR                  [	        U[
        5      (       a  S[        R                  " U5       3OSU 35        SSR                  U5      -   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`: r   zid: {r   })r   rr   dumpsrq   r   r*   r   )r   r   r   r   r   props         r   _format_propertiesAGEGraph._format_properties  s     $$&DAqcTZZ]O,DLL ' >d*4LL+5b#+>+>$tzz"~&'d2$K TYYu%%++r   c                N    [         R                  " [        R                  SU 5      $ )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   )r   subr3   r?   )ry   s    r   clean_graph_labelsAGEGraph.clean_graph_labels  s     vvh**C77r   c           
        U(       d  SOSnSnU GH  nU(       a}  UR                   R                  R                  S5      (       dS  [        UR                   R                  R                  S5      5      R                  5       UR                   R                  S'   UR                   H  nUR                  UR                  S'   U(       aX  UR                  UR                  U R                  UR                  5      U R                  UR                   R                  5      S9nOQUR                  [        R                  UR                  5      U R                  UR                  5      UR                  S9nU R                  U5        M     UR                    GHb  nUR                   R                  UR                   R                  S'   UR"                  R                  UR"                  R                  S'   [        R                  UR                   R                  5      U R                  UR                   R                  5      [        R                  UR"                  R                  5      U R                  UR"                  R                  5      [        R                  UR                  5      R%                  5       U R                  UR                  5      S.n	UR                  " S
0 U	D6nU R                  U5        GMe     GM     g	)a  
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
z_
            MERGE (n:`{label}` {{`id`: "{id}"}})
            SET n = {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)ry   r   d_properties)ry   r   r   )f_labelf_propertiest_labelt_propertiesr_labelr_propertiesNr1   )sourcer   r   r   page_contentencode	hexdigestnodesr   r   rI   rn   r   r3   r  rc   r   targetr   )
r   graph_documentsinclude_sourcenode_insert_queryedge_insert_querydocnoderc   ru   inputss
             r   add_graph_documentsAGEGraph.add_graph_documents  s=   ( "	
 	 #Czz**..t4403

//66w?1ik JJ''-
 		(,%!-44"ii#'#:#:4??#K%)%<%<SZZ=P=P%Q 5 E .44&99$))D#'#:#:4??#K77 5 E 

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

5! *7 #r   )rG   rC   rO   r   r   )T)rC   r*   rQ   Dict[str, Any]rR   r>   r'   r(   )r'   z psycopg2.extras.NamedTupleCursor)r'   zTuple[List[str], List[str]])re   	List[str]r'   List[Dict[str, str]])re   r  r'   r  )r   r  r'   r  )rg   r  r'   List[Dict[str, Any]])re   r  r'   r  )r'   r(   r)   )r'   r  )r   r*   r   r<   r'   r*   )rc   r*   rC   r*   r'   r*   )r   r	   r'   r  )rc   r*   r   r   r'   r  r   )r   r  r   zUnion[str, None]r'   r*   )ry   r*   r'   r*   )F)r  zList[GraphDocument]r  r>   r'   r(   )r+   r,   r-   r.   r/   r   r   compiler?   __annotations__r   rH   rh   r|   r   staticmethodr   r   r   rP   r   r   r   r   r   r   rc   r   r  r  r0   r1   r   r   r3   r3   !   s   . E ::o6K6 EI<"<"%3<"=A<"	<"|"",9v-"  $FPEN
>   & & < <0 J
 J
X 4 4l 02 ,\ ;?,",(8,	, ,2 
8 
8 LQK"2K"DHK"	K" K"r   r3   )
__future__r   rr   r   hashlibr   typingr   r   r   r   r	   r
   r   r   )langchain_community.graphs.graph_documentr   &langchain_community.graphs.graph_storer   r\   rD   rN   r   r3   r1   r   r   <module>r&     s@    "  	  T T T C =	 $\"z \"r   