diffgenome 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diffgenome/__init__.py +7 -0
- diffgenome/__main__.py +240 -0
- diffgenome/_collectors/go/dg/dg.go +623 -0
- diffgenome/_collectors/go/go.mod +3 -0
- diffgenome/_collectors/go/instrument/facts.go +346 -0
- diffgenome/_collectors/go/instrument/main.go +484 -0
- diffgenome/_collectors/node/instrument.js +289 -0
- diffgenome/_collectors/node/jest-setup.js +40 -0
- diffgenome/_collectors/node/package-lock.json +35 -0
- diffgenome/_collectors/node/package.json +11 -0
- diffgenome/_collectors/node/runtime.js +426 -0
- diffgenome/ambiguity.py +122 -0
- diffgenome/api.py +67 -0
- diffgenome/change.py +86 -0
- diffgenome/change_artifact.py +310 -0
- diffgenome/collect/__init__.py +2 -0
- diffgenome/collect/go_test.py +271 -0
- diffgenome/collect/node_jest.py +319 -0
- diffgenome/collect/py_monitoring.py +985 -0
- diffgenome/collect/py_runtime.py +116 -0
- diffgenome/collect/py_symbols.py +238 -0
- diffgenome/collect/pytest_plugin.py +130 -0
- diffgenome/compose.py +469 -0
- diffgenome/dependence.py +264 -0
- diffgenome/evaluate.py +669 -0
- diffgenome/frontends/__init__.py +0 -0
- diffgenome/frontends/python_ir.py +335 -0
- diffgenome/genome.py +1016 -0
- diffgenome/genome_pipeline.py +674 -0
- diffgenome/genome_prompt.py +33 -0
- diffgenome/genome_state.py +2118 -0
- diffgenome/graph.py +426 -0
- diffgenome/llm.py +189 -0
- diffgenome/model.py +364 -0
- diffgenome/mvp.py +398 -0
- diffgenome/probe.py +509 -0
- diffgenome/projection.py +308 -0
- diffgenome/py.typed +0 -0
- diffgenome/render.py +118 -0
- diffgenome/report.py +363 -0
- diffgenome/resolve.py +37 -0
- diffgenome/runtime.py +74 -0
- diffgenome/runtime_evidence.py +261 -0
- diffgenome/sandbox.py +166 -0
- diffgenome/serialize.py +96 -0
- diffgenome/sites.py +19 -0
- diffgenome/static_types.py +69 -0
- diffgenome/structure.py +462 -0
- diffgenome-0.1.0.dist-info/METADATA +139 -0
- diffgenome-0.1.0.dist-info/RECORD +53 -0
- diffgenome-0.1.0.dist-info/WHEEL +4 -0
- diffgenome-0.1.0.dist-info/entry_points.txt +2 -0
- diffgenome-0.1.0.dist-info/licenses/LICENSE +202 -0
diffgenome/__init__.py
ADDED
diffgenome/__main__.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from diffgenome import mvp
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def inspect_main(argv: list[str]) -> int:
|
|
10
|
+
ap = argparse.ArgumentParser(prog="diffgenome inspect")
|
|
11
|
+
ap.add_argument("--graph", required=True, type=Path, help="graph.json written by an mvp run")
|
|
12
|
+
ap.add_argument("--symbol", action="append", default=[], help="symbol id, or a suffix of one")
|
|
13
|
+
ap.add_argument("--up", type=int, default=3)
|
|
14
|
+
ap.add_argument("--down", type=int, default=4)
|
|
15
|
+
ap.add_argument("--json", action="store_true", help="print the neighborhood as JSON")
|
|
16
|
+
ap.add_argument(
|
|
17
|
+
"--ambiguity", action="store_true", help="print the ambiguity/rejected-join report"
|
|
18
|
+
)
|
|
19
|
+
ap.add_argument(
|
|
20
|
+
"--behavior",
|
|
21
|
+
action="store_true",
|
|
22
|
+
help="print the behavioral projection instead of the evidence slice",
|
|
23
|
+
)
|
|
24
|
+
args = ap.parse_args(argv)
|
|
25
|
+
from diffgenome.graph import BehavioralGraph
|
|
26
|
+
from diffgenome.report import render_map_slice
|
|
27
|
+
|
|
28
|
+
graph = BehavioralGraph.from_json(json.loads(args.graph.read_text()))
|
|
29
|
+
if args.ambiguity:
|
|
30
|
+
from diffgenome.ambiguity import render_ambiguity
|
|
31
|
+
|
|
32
|
+
print(render_ambiguity(graph))
|
|
33
|
+
return 0
|
|
34
|
+
if not args.symbol:
|
|
35
|
+
print(f"{len(graph.symbols)} symbols, {len(graph.edges)} edges, {len(graph.gaps)} gaps")
|
|
36
|
+
return 0
|
|
37
|
+
seeds: list[str] = []
|
|
38
|
+
for q in args.symbol:
|
|
39
|
+
matches = [s for s in graph.symbols if s == q or s.endswith(q)]
|
|
40
|
+
if not matches:
|
|
41
|
+
print(f"no symbol matches {q!r}", file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
seeds.extend(sorted(matches))
|
|
44
|
+
if args.json:
|
|
45
|
+
nb = graph.neighborhood(seeds, up=args.up, down=args.down)
|
|
46
|
+
print(
|
|
47
|
+
json.dumps(
|
|
48
|
+
{
|
|
49
|
+
"seeds": seeds,
|
|
50
|
+
"metrics": nb.metrics().as_dict(),
|
|
51
|
+
"edges": [
|
|
52
|
+
{
|
|
53
|
+
"distance": d,
|
|
54
|
+
"caller": e.caller,
|
|
55
|
+
"callee": e.callee,
|
|
56
|
+
"kind": e.kind.value,
|
|
57
|
+
"best_join": e.best_join.name if e.best_join else None,
|
|
58
|
+
"executions": sorted(e.executions),
|
|
59
|
+
"probe_derived": e.probe_derived,
|
|
60
|
+
}
|
|
61
|
+
for d, e in sorted(
|
|
62
|
+
nb.edges.values(), key=lambda x: (x[0], x[1].caller, x[1].callee)
|
|
63
|
+
)
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
indent=1,
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
elif args.behavior:
|
|
70
|
+
from diffgenome.projection import render_behavior_map
|
|
71
|
+
|
|
72
|
+
print(render_behavior_map(graph, seeds, up=args.up, down=args.down))
|
|
73
|
+
else:
|
|
74
|
+
print(render_map_slice(graph, seeds, up=args.up, down=args.down))
|
|
75
|
+
return 0
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def evaluate_main(argv: list[str]) -> int:
|
|
79
|
+
ap = argparse.ArgumentParser(prog="diffgenome evaluate")
|
|
80
|
+
ap.add_argument("--graph", required=True, type=Path, help="reconstructed graph.json")
|
|
81
|
+
ap.add_argument("--ground-truth", required=True, type=Path, help="directory of complete traces")
|
|
82
|
+
ap.add_argument("--entry", action="append", required=True, help="entry symbol (exact id)")
|
|
83
|
+
ap.add_argument("--depth", type=int, default=6)
|
|
84
|
+
ap.add_argument("--json", type=Path, default=None, help="also write the evaluation as JSON")
|
|
85
|
+
ap.add_argument(
|
|
86
|
+
"--traces",
|
|
87
|
+
action="append",
|
|
88
|
+
default=[],
|
|
89
|
+
type=Path,
|
|
90
|
+
help="directories of the executions the graph was built from (for seam grading)",
|
|
91
|
+
)
|
|
92
|
+
ap.add_argument(
|
|
93
|
+
"--no-state",
|
|
94
|
+
action="store_true",
|
|
95
|
+
help="rebuild the graph from --traces ignoring state facts (the VALUE-only baseline)",
|
|
96
|
+
)
|
|
97
|
+
args = ap.parse_args(argv)
|
|
98
|
+
from diffgenome.evaluate import (
|
|
99
|
+
evaluate,
|
|
100
|
+
ground_truth_from,
|
|
101
|
+
render_evaluation,
|
|
102
|
+
render_seams,
|
|
103
|
+
seam_precision,
|
|
104
|
+
)
|
|
105
|
+
from diffgenome.graph import BehavioralGraph
|
|
106
|
+
from diffgenome.model import Origin
|
|
107
|
+
from diffgenome.serialize import execution_from_json
|
|
108
|
+
|
|
109
|
+
graph = BehavioralGraph.from_json(json.loads(args.graph.read_text()))
|
|
110
|
+
if args.no_state:
|
|
111
|
+
from diffgenome.compose import build_corpus as _build_corpus
|
|
112
|
+
from diffgenome.graph import build_graph as _build_graph
|
|
113
|
+
|
|
114
|
+
runs = [
|
|
115
|
+
execution_from_json(f.read_text())
|
|
116
|
+
for d in args.traces
|
|
117
|
+
for f in sorted(d.glob("*.json"))
|
|
118
|
+
]
|
|
119
|
+
graph = _build_graph(_build_corpus(runs), use_state=False)
|
|
120
|
+
print("(graph rebuilt from --traces with state facts ignored: VALUE-only baseline)\n")
|
|
121
|
+
truth_runs = [
|
|
122
|
+
execution_from_json(f.read_text()) for f in sorted(args.ground_truth.glob("*.json"))
|
|
123
|
+
]
|
|
124
|
+
origins = {s.id: s.origin for e in truth_runs for s in e.symbols}
|
|
125
|
+
origins.update(
|
|
126
|
+
{sid: s.origin for sid, s in graph.symbols.items() if s.origin is not Origin.UNKNOWN}
|
|
127
|
+
)
|
|
128
|
+
gt = ground_truth_from(truth_runs, args.entry, origins)
|
|
129
|
+
ev = evaluate(graph, gt, args.entry, depth=args.depth)
|
|
130
|
+
print(render_evaluation(ev))
|
|
131
|
+
doc = ev.as_dict()
|
|
132
|
+
if args.traces:
|
|
133
|
+
# path level needs the corpus behind the graph
|
|
134
|
+
from diffgenome.compose import build_corpus
|
|
135
|
+
from diffgenome.evaluate import (
|
|
136
|
+
evaluate_paths,
|
|
137
|
+
explain_paths,
|
|
138
|
+
join_matrix,
|
|
139
|
+
render_matrix,
|
|
140
|
+
render_paths,
|
|
141
|
+
)
|
|
142
|
+
from diffgenome.graph import build_graph
|
|
143
|
+
|
|
144
|
+
corpus_runs = [
|
|
145
|
+
execution_from_json(f.read_text())
|
|
146
|
+
for d in args.traces
|
|
147
|
+
for f in sorted(d.glob("*.json"))
|
|
148
|
+
]
|
|
149
|
+
full = build_graph(build_corpus(corpus_runs), use_state=not args.no_state)
|
|
150
|
+
path_docs: list[dict[str, object]] = []
|
|
151
|
+
for entry in args.entry:
|
|
152
|
+
pe = evaluate_paths(full, truth_runs, entry, origins)
|
|
153
|
+
print(render_paths(pe))
|
|
154
|
+
explanations = explain_paths(pe, full)
|
|
155
|
+
for line in explanations:
|
|
156
|
+
print(" " + line)
|
|
157
|
+
print()
|
|
158
|
+
path_docs.append(
|
|
159
|
+
{
|
|
160
|
+
"entry": entry,
|
|
161
|
+
"truth": pe.truth_paths,
|
|
162
|
+
"claimed": pe.claimed_paths,
|
|
163
|
+
"matched": pe.matched,
|
|
164
|
+
"extra": [list(p) for p in pe.extra],
|
|
165
|
+
"missed": [list(p) for p in pe.missed],
|
|
166
|
+
"outcome_mismatches": pe.outcome_mismatches,
|
|
167
|
+
"explanations": explanations,
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
doc["paths"] = path_docs
|
|
171
|
+
if args.traces:
|
|
172
|
+
from diffgenome.model import CallNode, Execution
|
|
173
|
+
|
|
174
|
+
def _repo_edges(ex: Execution) -> set[tuple[str, str]]:
|
|
175
|
+
by_id = {n.id: n for n in ex.nodes}
|
|
176
|
+
edges: set[tuple[str, str]] = set()
|
|
177
|
+
for n in ex.nodes:
|
|
178
|
+
if isinstance(n, CallNode) and n.parent is not None:
|
|
179
|
+
p = by_id[n.parent]
|
|
180
|
+
if (
|
|
181
|
+
isinstance(p, CallNode)
|
|
182
|
+
and origins.get(p.symbol) is Origin.REPO
|
|
183
|
+
and origins.get(n.symbol) is Origin.REPO
|
|
184
|
+
):
|
|
185
|
+
edges.add((p.symbol, n.symbol))
|
|
186
|
+
return edges
|
|
187
|
+
|
|
188
|
+
per_exec = {ex.id: _repo_edges(ex) for ex in corpus_runs}
|
|
189
|
+
truth_per_exec = [_repo_edges(ex) for ex in truth_runs]
|
|
190
|
+
grades = seam_precision(graph, per_exec, gt)
|
|
191
|
+
print("## Join lattice: seam-level precision")
|
|
192
|
+
print(render_seams(grades))
|
|
193
|
+
doc["seam_grades"] = [g.__dict__ for g in grades]
|
|
194
|
+
rows = join_matrix(full, per_exec, gt, truth_per_exec)
|
|
195
|
+
print("## Join matrix (seams on ground-truth paths)")
|
|
196
|
+
print(render_matrix(rows))
|
|
197
|
+
doc["join_matrix"] = [r.__dict__ for r in rows]
|
|
198
|
+
if args.json:
|
|
199
|
+
args.json.write_text(json.dumps(doc, indent=1) + "\n")
|
|
200
|
+
return 0
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def main(argv: list[str] | None = None) -> int:
|
|
204
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
205
|
+
if argv[:1] in (["--version"], ["-V"]):
|
|
206
|
+
from diffgenome import __version__
|
|
207
|
+
|
|
208
|
+
print(f"diffgenome {__version__}")
|
|
209
|
+
return 0
|
|
210
|
+
if argv and argv[0] == "inspect":
|
|
211
|
+
return inspect_main(argv[1:])
|
|
212
|
+
if argv and argv[0] == "evaluate":
|
|
213
|
+
return evaluate_main(argv[1:])
|
|
214
|
+
if argv and argv[0] == "change":
|
|
215
|
+
# The integrator's entry point: same pipeline as `mvp`, existing tests only by
|
|
216
|
+
# default (no LLM call), and the diffgenome-change/1 artifact as the product.
|
|
217
|
+
rest = argv[1:]
|
|
218
|
+
if "--writer" not in rest:
|
|
219
|
+
rest = ["--writer", "none", *rest]
|
|
220
|
+
if "--probes" not in rest:
|
|
221
|
+
rest = ["--probes", "0", *rest]
|
|
222
|
+
if "--up" not in rest:
|
|
223
|
+
# integrators ask "what reaches the change": follow callers far enough to meet
|
|
224
|
+
# an entry point even when the change sits deep below it
|
|
225
|
+
rest = ["--up", "5", *rest]
|
|
226
|
+
if "--probe-max-distance" not in rest:
|
|
227
|
+
# a probe is only worth its cost when the gap sits next to the change
|
|
228
|
+
rest = ["--probe-max-distance", "1", *rest]
|
|
229
|
+
return mvp.main(rest)
|
|
230
|
+
if argv and argv[0] == "genome":
|
|
231
|
+
from diffgenome import genome_pipeline
|
|
232
|
+
|
|
233
|
+
return genome_pipeline.main(argv[1:])
|
|
234
|
+
if argv and argv[0] == "mvp":
|
|
235
|
+
argv = argv[1:]
|
|
236
|
+
return mvp.main(argv)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
if __name__ == "__main__":
|
|
240
|
+
sys.exit(main())
|