candle-fire / graph /serializer.py
KevinIsInCoding
feat: Stage 4 — entity extraction + knowledge graph + KG-expanded RAG
42fb3af
Raw
History Blame Contribute Delete
1.88 kB
"""Save and load the ALS knowledge graph (pickle for speed, JSON for inspection)."""
from __future__ import annotations
import json
import pickle
from pathlib import Path
import networkx as nx
from config import GRAPH_JSON_PATH, GRAPH_PICKLE_PATH
from logging_config import get_logger
_logger = get_logger("graph.serializer")
def save_graph(
G: nx.DiGraph,
pickle_path: Path = GRAPH_PICKLE_PATH,
json_path: Path = GRAPH_JSON_PATH,
) -> None:
for path in (pickle_path, json_path):
path.parent.mkdir(parents=True, exist_ok=True)
with open(pickle_path, "wb") as f:
pickle.dump(G, f, protocol=pickle.HIGHEST_PROTOCOL)
data = {
"nodes": [
{"id": n, **{k: _json_safe(v) for k, v in G.nodes[n].items()}}
for n in G.nodes
],
"edges": [
{"source": u, "target": v, **{k: _json_safe(dv) for k, dv in d.items()}}
for u, v, d in G.edges(data=True)
],
"stats": {
"nodes": G.number_of_nodes(),
"edges": G.number_of_edges(),
},
}
json_path.write_text(json.dumps(data, indent=2))
_logger.info(f"Saved graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges")
def load_graph(pickle_path: Path = GRAPH_PICKLE_PATH) -> nx.DiGraph:
if not pickle_path.exists():
raise FileNotFoundError(
f"Graph not found at {pickle_path}. "
"Run: uv run python scripts/build_graph.py"
)
with open(pickle_path, "rb") as f:
G = pickle.load(f)
_logger.info(f"Loaded graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges")
return G
def _json_safe(value: object) -> object:
if isinstance(value, set):
return list(value)
if isinstance(value, (list, dict, str, int, float, bool)) or value is None:
return value
return str(value)