atypique-api / core /causal_graph.py
RDS777's picture
Backend v3.0 complet
8e3a425
Raw
History Blame Contribute Delete
2.41 kB
"""core/causal_graph.py — Graphe causal NetworkX"""
import networkx as nx, hashlib, time, sqlite3, os
from typing import List, Dict, Optional
from pathlib import Path
DATA_DIR = Path(os.environ.get("DATA_DIR", "/tmp/vortex_data"))
class CausalGraph:
def __init__(self):
self.graph = nx.MultiDiGraph()
self._db = None
def _get_db(self):
if self._db is None:
DATA_DIR.mkdir(parents=True, exist_ok=True)
self._db = sqlite3.connect(str(DATA_DIR / "causal.db"), check_same_thread=False)
self._db.execute(
"CREATE TABLE IF NOT EXISTS causal_edges "
"(id TEXT PRIMARY KEY, src TEXT, tgt TEXT, relation TEXT, weight REAL, ts REAL)"
)
return self._db
def add_node(self, nid: str, kind: str, attrs: Optional[Dict] = None):
self.graph.add_node(nid, kind=kind, **(attrs or {}))
def add_edge(self, src: str, tgt: str, relation: str = "causes", weight: float = 1.0):
eid = hashlib.md5(f"{src}{tgt}{relation}".encode()).hexdigest()[:12]
for n in (src, tgt):
if n not in self.graph:
self.graph.add_node(n, kind="unknown")
self.graph.add_edge(src, tgt, relation=relation, weight=weight)
db = self._get_db()
db.execute(
"INSERT OR REPLACE INTO causal_edges VALUES (?,?,?,?,?,?)",
(eid, src, tgt, relation, weight, time.time())
)
db.commit()
return True
def causal_query(self, src: str, tgt: Optional[str] = None, max_depth: int = 3) -> List[Dict]:
if src not in self.graph:
return []
try:
if tgt and tgt in self.graph:
paths = list(nx.all_simple_paths(self.graph, src, tgt, cutoff=max_depth))
return [{"path": p, "length": len(p)} for p in paths[:10]]
result = []
for node in nx.bfs_tree(self.graph, src, depth_limit=max_depth).nodes():
if node != src:
result.append({
"node": node,
"depth": nx.shortest_path_length(self.graph, src, node)
})
return result[:20]
except Exception as e:
return [{"error": str(e)}]
def stats(self):
return {"nodes": self.graph.number_of_nodes(), "edges": self.graph.number_of_edges()}