
    מi*9                       U d Z ddlmZ ddlZddlZddlmZmZmZm	Z	  ej                  e      Z	 ddlmZ dZ G d	 d
      Zdaded<   	 	 	 d	 	 	 	 	 	 	 ddZddZddZy# e$ r dZdZej%                  d       Y Ew xY w)ud  
core/graph/client.py — FalkorDB client wrapper for Genesis Knowledge Graph.

GenesisGraph wraps the FalkorDB Python client and exposes a clean interface
for CRUD operations and Cypher traversals.  If the ``falkordb`` package is
not installed the class degrades gracefully: all read methods return empty
lists and write methods are no-ops, with a single logged warning.

Connection defaults (overridden by env vars):
    FALKORDB_HOST  — defaults to "localhost"
    FALKORDB_PORT  — defaults to 6379
    FALKORDB_GRAPH — graph name, defaults to "genesis_kg"

Production FalkorDB is on the Sunaiva Memory VPS:
    host: 152.53.201.221, port: 6379, graph: sunaiva_memory

# VERIFICATION_STAMP
# Story: M9.02 — core/graph/client.py — GenesisGraph + get_graph()
# Verified By: parallel-builder
# Verified At: 2026-02-25T00:00:00Z
# Tests: 8/8
# Coverage: 100%
    )annotationsN)AnyDictListOptional)FalkorDBTFun   falkordb package not installed — GenesisGraph running in no-op/stub mode. Install with: pip install falkordbc                      e Zd ZdZ	 	 	 d	 	 	 	 	 	 	 ddZddZddZ	 d	 	 	 	 	 ddZ	 d	 	 	 	 	 	 	 ddZ	 d	 	 	 	 	 	 	 	 	 ddZ		 	 d	 	 	 	 	 	 	 dd	Z
	 	 	 d	 	 	 	 	 	 	 dd
ZddZy)GenesisGraphz
    Thin wrapper around a FalkorDB graph.

    All methods return empty lists / None when the falkordb package is absent
    instead of raising ImportError, so callers can import GenesisGraph
    unconditionally.
    c                   t         j                  j                  d|      | _        t	        t         j                  j                  d|            | _        t         j                  j                  d|      | _        d | _        d | _        y )NFALKORDB_HOSTFALKORDB_PORTFALKORDB_GRAPH)	osenvirongethostintport
graph_name_db_graph)selfr   r   r   s       */mnt/e/genesis-system/core/graph/client.py__init__zGenesisGraph.__init__?   sZ     JJNN?D9	

=>	**..)9:F"&%)    c                   t         sy| j                  y	 t        | j                  | j                        | _        | j
                  j                  | j                        | _        t        j                  d| j                  | j                  | j                         y# t        $ r.}t        j                  d|       d| _        d| _        Y d}~yd}~ww xY w)zr
        Lazily establish connection to FalkorDB.

        Returns True on success, False if unavailable.
        FNT)r   r   z'Connected to FalkorDB at %s:%d graph=%szFalkorDB connect failed: %s)_FALKORDB_AVAILABLEr   	_FalkorDBr   r   r   select_graphr   loggerdebug	Exceptionwarning)r   excs     r   connectzGenesisGraph.connectO   s     #;;"	 diidii@DH((//@DKLL9					  	NN8#>DHDK		s   BB 	C&$CCc                <    | j                   y| j                         S )zAConnect if not yet connected. Returns True if graph is available.T)r   r%   )r   s    r   _ensure_connectedzGenesisGraph._ensure_connectedi   s    ;;"||~r   Nc                   | j                         sg S 	 | j                  j                  ||xs i       }g }|j                  s|S |j                  D ]]  }i }t	        |j
                  |      D ]/  \  }}t        |d      rt        |j                        ||<   +|||<   1 |j                  |       _ |S # t        $ r&}	t        j                  d|	|dd        g cY d}	~	S d}	~	ww xY w)u  
        Execute a Cypher query and return results as a list of dicts.

        Each dict maps column name → value (node properties for node columns,
        scalar value for scalar columns).

        Returns empty list when FalkorDB is unavailable or query fails.
        
propertiesz%FalkorDB query failed: %s | cypher=%sNx   )r'   r   query
result_setzipheaderhasattrdictr)   appendr"   r    r#   )
r   cypherparamsresultrowsrecordrowcol_namevaluer$   s
             r   r+   zGenesisGraph.querys   s     %%'I	[[&&vv|<F)+D$$ ++ !&('*6==&'A .OHeul3(,U-=-=(>H(-H. C ! K 	NNBCPTQTVI	s$   /B2 A-B2 2	C!;CC!C!c                   | j                         syt        |xs i       }||d<   |j                         D ci c]  \  }}|t        |       }}}dj	                  d |D              }t        |      }	d|	 d| d}
	 | j                  j                  |
|       yc c}}w # t        $ r!}t        j                  d	||       Y d
}~yd
}~ww xY w)z
        MERGE a node with the given id / type label.

        ``entity_type`` becomes the node label (e.g. "axiom", "entity").
        All string-serialisable properties are stored on the node.

        Returns True on success, False on failure.
        Fidz, c              3  ,   K   | ]  }d | d|   yw)n. = $N .0ks     r   	<genexpr>z*GenesisGraph.add_entity.<locals>.<genexpr>   s     DA"QCtA3Ds   z	MERGE (n:z {id: $id}) SET z	 RETURN nTzadd_entity failed: %s | id=%sN)r'   r0   items_to_primitivejoin_sanitise_labelr   r+   r"   r    r#   )r   	entity_identity_typer)   propsrB   v
safe_propsset_clauseslabelr2   r$   s               r   
add_entityzGenesisGraph.add_entity   s     %%'Z%2&d6;kkmDdaaq))D
D iiDDD,w -   	
	KKfj1 E  	NN:CK	s   B?B" "	C+CCc                   | j                         syt        |      }t        |xs i       }|j                         D ci c]  \  }}|t	        |       }	}}||d}
|
j                  |	       |	rddj                  d |	D              z   }nd}d| d| d	}	 | j                  j                  ||
       y
c c}}w # t        $ r#}t        j                  d||||       Y d}~yd}~ww xY w)a  
        CREATE a directed relationship between two existing nodes.

        Both nodes must already exist (identified by their ``id`` property).
        ``rel_type`` is used as the relationship type label.

        Returns True on success, False on failure.
        F)from_idto_idz SET r.z, r.c              3  *   K   | ]  }| d |   yw)r>   Nr?   r@   s     r   rC   z0GenesisGraph.add_relationship.<locals>.<genexpr>   s      /"#1#T!/s    z9MATCH (a {id: $from_id}), (b {id: $to_id}) CREATE (a)-[r:z]->(b)z	 RETURN rTz+add_relationship failed: %s | %s -[%s]-> %sN)r'   rG   r0   rD   rE   updaterF   r   r+   r"   r    r#   )r   rQ   rR   rel_typer)   safe_relrJ   rB   rK   rL   cypher_paramsset_partr2   r$   s                 r   add_relationshipzGenesisGraph.add_relationship   s    %%'"8,Z%2&6;kkmDdaaq))D
D 5<e(LZ( 6;; /'1/ $ H H%Jfj  		KKfm4+ E,  	NN= 	s   B5B; ;	C'C""C'c                p   | j                         sg S t        dt        |d            }|rdt        |       nd}d| d| d}	 | j                  j                  |d|i      }g }|j                  s|S |j                  D ]  }|d	   }	t        |      dkD  r|d   nd
}
i }t        |	d      r|j                  |	j                         t        |	d      r|	j                  r|	j                  d	   |d<   |
|d<   |j                  |        |S # t        $ r#}t        j                  d||       g cY d
}~S d
}~ww xY w)u5  
        Return neighboring nodes reachable from *entity_id*.

        ``rel_type``  — if supplied, only follow relationships of that type.
        ``depth``     — traversal depth (default 1; capped at 5 for safety).

        Returns list of dicts, each with "id", "type" and all node properties.
              :rT   zMATCH (a {id: $id})-[rz*1..z1]->(b) RETURN b, type(r[0]) as rel_type LIMIT 100r;   r   Nr)   labelstype	_rel_typez get_neighbors failed: %s | id=%s)r'   maxminrG   r   r+   r,   lenr/   rU   r)   r_   r1   r"   r    r#   )r   rH   rV   depth
rel_filterr2   r4   r5   r6   noderel_str	node_dictr$   s                r   get_neighborszGenesisGraph.get_neighbors   sE    %%'IAs5!}%8@q234b
&zl$ug >9 : 		[[&&vi/@AF)+D$$ ++ 
'ay'*6{Q&)D,.	4.$$T__54*t{{(,AIf%)0	+&I&
' K 	NN=sINI	s%   -D	 3BD	 		D5D0*D50D5c                   | j                         sg S i }|rt        |      }d| d}nd}g }|rB|j                         D ]/  \  }}	d| }
|j                  d| d|
        t	        |	      ||
<   1 |rddj                  |       nd	}||d
<   | | d}	 | j                  j                  ||      }g }|j                  s|S |j                  D ]k  }|d   }i }t        |d      r|j                  |j                         t        |d      r|j                  r|j                  d   |d<   |j                  |       m |S # t        $ r"}t        j                  d|       g cY d}~S d}~ww xY w)z
        Search for nodes, optionally filtering by type label and/or properties.

        Returns list of property dicts.
        z	MATCH (n:)z	MATCH (n)filter_r=   r>   z WHERE z AND rT   limitz RETURN n LIMIT $limitr   r)   r_   r`   zsearch_entities failed: %sN)r'   rG   rD   r1   rE   rF   r   r+   r,   r/   rU   r)   r_   r"   r    r#   )r   rI   property_filterrn   r3   rN   match_clausewhere_partsrB   rK   	param_keywhere_clauser2   r4   r5   r6   rg   ri   r$   s                      r   search_entitieszGenesisGraph.search_entities#  s    %%'I!##K0E&ugQ/L&L!#'--/ 51%aSM	""Rs$yk#:;$1!$4y!5
 ALk!: ;<QSw >,/EF	[[&&vv6F)+D$$ ++ 'ay,.	4.$$T__54*t{{(,AIf%I&' K 	NN7=I	s%   +D> A;D> >	E)E$E)$E)c                    d| _         | j                  K	 t        | j                  dd      xs t        | j                  dd      }||j                          d| _        yy# t        $ r Y w xY w)z&Release FalkorDB connection resources.N
connection_redis)r   r   getattrcloser"   )r   conns     r   ry   zGenesisGraph.closeZ  su    88txxt< HHhA #JJL DH    s   AA   	A,+A,	localhosti  
genesis_kg)r   strr   r   r   r~   returnNone)r   bool)N)r2   r~   r3   Optional[Dict[str, Any]]r   List[Dict[str, Any]])rH   r~   rI   r~   r)   r   r   r   )
rQ   r~   rR   r~   rV   r~   r)   r   r   r   )Nr\   )rH   r~   rV   Optional[str]re   r   r   r   )NNd   )rI   r   ro   r   rn   r   r   r   )r   r   )__name__
__module____qualname____doc__r   r%   r'   r+   rO   rZ   rj   rt   ry   r?   r   r   r
   r
   6   sJ     &	
*
* 
* 	
*
 

* 4 ,0!! )! 
	!V 04	## # -	#
 
#T 0422 2 	2
 -2 
2v #'	**  * 	*
 
*\ &*48	1"1 21 	1
 
1nr   r
   zOptional[GenesisGraph]_default_graphc                6    t         t        | ||      a t         S )z
    Return (and lazily create) the module-level GenesisGraph singleton.

    Environment variables FALKORDB_HOST / FALKORDB_PORT / FALKORDB_GRAPH
    override the defaults at construction time.
    r   r   r   )r   r
   r   s      r   	get_graphr   q  s     %4dzRr   c                r    | j                  dd      j                  dd      j                  dd      }|r|S dS )z
    Convert a label string to a Cypher-safe identifier.

    Replaces spaces and hyphens with underscores; upper-cases the first char
    to match Cypher conventions.
     _-.Node)replace)rN   safes     r   rG   rG     s;     ==c"**34<<S#FD4#V#r   c                   ddl }t        | t        t        t        t
        f      r| S t        | t        t        f      r| D cg c]  }t        |       c}S 	 |j                  |       S c c}w # t        t        f$ r t        |       cY S w xY w)z
    Convert complex Python objects to FalkorDB-compatible primitive types.

    FalkorDB supports: str, int, float, bool, list of primitives.
    Anything else is JSON-serialised to a string.
    r   N)json
isinstancer~   r   floatr   listtuplerE   dumps	TypeError
ValueError)r9   r   rK   s      r   rE   rE     s|     %#sE401%$'*/0Qa 00zz%   1 z" 5zs   A(A- -B
	B
r{   )r   r~   r   r   r   r~   r   r
   )rN   r~   r   r~   )r9   r   r   r   )r   
__future__r   loggingr   typingr   r   r   r   	getLoggerr   r    falkordbr   r   r   ImportErrorr#   r
   r   __annotations__r   rG   rE   r?   r   r   <module>r      s   . #  	 , ,			8	$
.q qp	 *.& - "

  	,$U  I
NN	-s   A" "A?>A?