Spaces:
Running
Running
| """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) | |