Measure token reduction: corpus tokens vs graphify query tokens. Args: graph_path: path to the built graph corpus_words: total word count from detect() output; if None, estimated from graph questions: list of questions to benchmark; defaults to _SAMPLE_QUESTIONS Ret
(
graph_path: str | None = None,
corpus_words: int | None = None,
questions: list[str] | None = None,
)
| 86 | |
| 87 | |
| 88 | def run_benchmark( |
| 89 | graph_path: str | None = None, |
| 90 | corpus_words: int | None = None, |
| 91 | questions: list[str] | None = None, |
| 92 | ) -> dict: |
| 93 | """Measure token reduction: corpus tokens vs graphify query tokens. |
| 94 | |
| 95 | Args: |
| 96 | graph_path: path to the built graph |
| 97 | corpus_words: total word count from detect() output; if None, estimated from graph |
| 98 | questions: list of questions to benchmark; defaults to _SAMPLE_QUESTIONS |
| 99 | |
| 100 | Returns dict with: corpus_tokens, avg_query_tokens, reduction_ratio, per_question |
| 101 | """ |
| 102 | graph_path = graph_path or _default_graph_json() |
| 103 | from graphify.security import check_graph_file_size_cap |
| 104 | check_graph_file_size_cap(Path(graph_path)) |
| 105 | data = json.loads(Path(graph_path).read_text(encoding="utf-8")) |
| 106 | try: |
| 107 | G = json_graph.node_link_graph(data, edges="links") |
| 108 | except TypeError: |
| 109 | G = json_graph.node_link_graph(data) |
| 110 | |
| 111 | if corpus_words is None: |
| 112 | # Rough estimate: each node label is ~3 words, plus source context |
| 113 | corpus_words = G.number_of_nodes() * 50 |
| 114 | |
| 115 | corpus_tokens = corpus_words * 100 // 75 # words → tokens (100 words ≈ 133 tokens) |
| 116 | |
| 117 | qs = questions or _SAMPLE_QUESTIONS |
| 118 | per_question = [] |
| 119 | for q in qs: |
| 120 | qt = _query_subgraph_tokens(G, q) |
| 121 | if qt > 0: |
| 122 | per_question.append({"question": q, "query_tokens": qt, "reduction": round(corpus_tokens / qt, 1)}) |
| 123 | |
| 124 | if not per_question: |
| 125 | return {"error": "No matching nodes found for sample questions. Build the graph first."} |
| 126 | |
| 127 | avg_query_tokens = sum(p["query_tokens"] for p in per_question) // len(per_question) |
| 128 | reduction_ratio = round(corpus_tokens / avg_query_tokens, 1) if avg_query_tokens > 0 else 0 |
| 129 | |
| 130 | return { |
| 131 | "corpus_tokens": corpus_tokens, |
| 132 | "corpus_words": corpus_words, |
| 133 | "nodes": G.number_of_nodes(), |
| 134 | "edges": G.number_of_edges(), |
| 135 | "avg_query_tokens": avg_query_tokens, |
| 136 | "reduction_ratio": reduction_ratio, |
| 137 | "per_question": per_question, |
| 138 | } |
| 139 | |
| 140 | |
| 141 | def print_benchmark(result: dict) -> None: |