align-agent 0.4.3__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.
- align_agent/__init__.py +11 -0
- align_agent/_audit/common.py +58 -0
- align_agent/_audit/deepswe.py +260 -0
- align_agent/_audit/excerpts.py +203 -0
- align_agent/_audit/extract.py +798 -0
- align_agent/_audit/materialize.py +624 -0
- align_agent/_audit/score.py +325 -0
- align_agent/_audit/shellparse.py +1141 -0
- align_agent/_audit/tarsur.py +153 -0
- align_agent/_vendor/src/__init__.py +3 -0
- align_agent/_vendor/src/harness/__init__.py +13 -0
- align_agent/_vendor/src/harness/arms.py +524 -0
- align_agent/_vendor/src/harness/completion.py +104 -0
- align_agent/_vendor/src/harness/dependency_notice.py +616 -0
- align_agent/_vendor/src/harness/freshness_hook.py +131 -0
- align_agent/_vendor/src/harness/lean.py +41 -0
- align_agent/_vendor/src/harness/mini_real.py +1040 -0
- align_agent/_vendor/src/harness/miniswe_scaffold.py +661 -0
- align_agent/_vendor/src/harness/tail_diff.py +292 -0
- align_agent/_vendor/src/harness/viewport.py +220 -0
- align_agent/_vendor/src/metrics/__init__.py +38 -0
- align_agent/_vendor/src/metrics/coherence.py +354 -0
- align_agent/_vendor/src/metrics/corruption_oracles.py +139 -0
- align_agent/_vendor/src/metrics/replay.py +399 -0
- align_agent/_vendor/src/metrics/statistics.py +185 -0
- align_agent/_vendor/src/metrics/verification.py +104 -0
- align_agent/_vendor/src/parsers/__init__.py +33 -0
- align_agent/_vendor/src/parsers/base_parser.py +349 -0
- align_agent/_vendor/src/parsers/file_events.py +806 -0
- align_agent/_vendor/src/parsers/miniswe_parser.py +96 -0
- align_agent/_vendor/src/parsers/openhands_parser.py +414 -0
- align_agent/_vendor/src/parsers/sweagent_parser.py +180 -0
- align_agent/_vendor/src/parsers/terminus_parser.py +106 -0
- align_agent/cli.py +101 -0
- align_agent/core.py +161 -0
- align_agent/run.py +84 -0
- align_agent/sessions.py +985 -0
- align_agent/writethrough/__init__.py +4 -0
- align_agent/writethrough/_lib/__init__.py +1 -0
- align_agent/writethrough/_lib/wt_src/__init__.py +3 -0
- align_agent/writethrough/_lib/wt_src/harness/__init__.py +1 -0
- align_agent/writethrough/_lib/wt_src/harness/dependency_notice.py +616 -0
- align_agent/writethrough/_lib/wt_src/harness/lean.py +41 -0
- align_agent/writethrough/_lib/wt_src/harness/tail_diff.py +292 -0
- align_agent/writethrough/_lib/wt_src/harness/viewport.py +220 -0
- align_agent/writethrough/_lib/wt_src/metrics/__init__.py +38 -0
- align_agent/writethrough/_lib/wt_src/metrics/replay.py +399 -0
- align_agent/writethrough/_lib/wt_src/parsers/__init__.py +1 -0
- align_agent/writethrough/_lib/wt_src/parsers/file_events.py +806 -0
- align_agent/writethrough/claude_code.py +81 -0
- align_agent/writethrough/engine.py +647 -0
- align_agent/writethrough/hooks.py +227 -0
- align_agent/writethrough/mcp_server.py +125 -0
- align_agent/writethrough/protocol.py +68 -0
- align_agent-0.4.3.dist-info/METADATA +224 -0
- align_agent-0.4.3.dist-info/RECORD +60 -0
- align_agent-0.4.3.dist-info/WHEEL +5 -0
- align_agent-0.4.3.dist-info/entry_points.txt +6 -0
- align_agent-0.4.3.dist-info/licenses/LICENSE +21 -0
- align_agent-0.4.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Recount on tarsur385/swebench-verified-trajectories (mini-swe-agent tool-call format). Usage: python3 scripts/audit/tarsur.py
|
|
2
|
+
|
|
3
|
+
Adapter: each assistant message's tool_calls (function 'bash', arguments.command)
|
|
4
|
+
is one step; its reply is the role:'tool' message with the same
|
|
5
|
+
tool_call_id ('Exit code: N' header gives the return code). Commands are
|
|
6
|
+
parsed with scripts/audit/shellparse.py, outcomes with extract.shell_outcome,
|
|
7
|
+
scored with score.Replay and classified with materialize.classify (automated
|
|
8
|
+
classifier only; no human labels exist for this corpus). Reports event
|
|
9
|
+
coverage first (steps with a tool call, ops per step, share of steps with no
|
|
10
|
+
op, unparsed commands), then per model: opportunities, hazards, structural
|
|
11
|
+
rate (Clopper-Pearson), automated classes, and hazards vs info.resolved.
|
|
12
|
+
Reads docs/audit/rq1/work/tarsur_raw/ (fetch_tarsur.py); writes work/tarsur.json.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import glob
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
from collections import Counter, defaultdict
|
|
21
|
+
|
|
22
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
23
|
+
sys.path.insert(0, HERE)
|
|
24
|
+
from common import WORK, dump # noqa: E402
|
|
25
|
+
import shellparse as sp # noqa: E402
|
|
26
|
+
from extract import shell_outcome, canonicalize, tok # noqa: E402
|
|
27
|
+
from score import Replay, cp_ci # noqa: E402
|
|
28
|
+
from materialize import classify # noqa: E402
|
|
29
|
+
|
|
30
|
+
RC = re.compile(r"^Exit code: (-?\d+)")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def adapt(d, cwd="/testbed"):
|
|
34
|
+
msgs = d.get("messages", [])
|
|
35
|
+
replies = {m.get("tool_call_id"): m for m in msgs if m.get("role") == "tool"}
|
|
36
|
+
steps, known, cov = [], {}, Counter()
|
|
37
|
+
for m in msgs:
|
|
38
|
+
if m.get("role") != "assistant":
|
|
39
|
+
continue
|
|
40
|
+
tcs = m.get("tool_calls") or []
|
|
41
|
+
if not tcs:
|
|
42
|
+
cov["assistant_without_tool_call"] += 1
|
|
43
|
+
continue
|
|
44
|
+
for tc in tcs:
|
|
45
|
+
cov["tool_calls"] += 1
|
|
46
|
+
fn = tc.get("function")
|
|
47
|
+
fn = fn.get("name") if isinstance(fn, dict) else fn
|
|
48
|
+
args = tc.get("arguments") or {}
|
|
49
|
+
if isinstance(args, str):
|
|
50
|
+
try:
|
|
51
|
+
args = json.loads(args)
|
|
52
|
+
except ValueError:
|
|
53
|
+
args = {}
|
|
54
|
+
cmd = args.get("command", "") if fn == "bash" else ""
|
|
55
|
+
cov[f"fn:{fn}"] += 1
|
|
56
|
+
r = replies.get(tc.get("id"))
|
|
57
|
+
obs = str((r or {}).get("content") or "")
|
|
58
|
+
mm = RC.match(obs)
|
|
59
|
+
rc = int(mm.group(1)) if mm else None
|
|
60
|
+
cov["reply_found"] += r is not None
|
|
61
|
+
ops, _, info = sp.extract(cmd, cwd, known, persist_cd=False) if cmd else ([], cwd, {})
|
|
62
|
+
cov["unparsed"] += bool(info.get("unparsed"))
|
|
63
|
+
if ops:
|
|
64
|
+
shell_outcome(ops, obs, rc, cmd)
|
|
65
|
+
steps.append({"i": len(steps), "unit": len(steps), "conv": 0, "bench": None, "tool": "shell",
|
|
66
|
+
"action": cmd, "thought": str(m.get("content") or "")[-1500:], "obs": obs, "rc": rc,
|
|
67
|
+
"cwd": cwd, "ran": True, "ops": ops, "tok_a": tok(cmd), "tok_o": tok(obs)})
|
|
68
|
+
return steps, cov
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def main():
|
|
72
|
+
base = os.path.join(WORK, "tarsur_raw")
|
|
73
|
+
per_model = defaultdict(Counter)
|
|
74
|
+
classes = defaultdict(Counter)
|
|
75
|
+
resolved = defaultdict(lambda: defaultdict(list))
|
|
76
|
+
cov_all = defaultdict(Counter)
|
|
77
|
+
for f in sorted(glob.glob(os.path.join(base, "*", "*.traj.json"))):
|
|
78
|
+
model = f.split(os.sep)[-2]
|
|
79
|
+
d = json.load(open(f))
|
|
80
|
+
steps, cov = adapt(d)
|
|
81
|
+
cov_all[model].update(cov)
|
|
82
|
+
t = {"traj_id": os.path.basename(f), "agent": "mini-SWE-agent-2.1", "model": model, "steps": steps,
|
|
83
|
+
"final_diff": (d.get("info") or {}).get("submission") or None}
|
|
84
|
+
t["root"] = canonicalize(steps)
|
|
85
|
+
r = Replay(t).run()
|
|
86
|
+
c = per_model[model]
|
|
87
|
+
c["trajectories"] += 1
|
|
88
|
+
c["steps"] += len(steps)
|
|
89
|
+
c["zero_op_steps"] += sum(1 for s in steps if not s["ops"])
|
|
90
|
+
c["ops"] += sum(len(s["ops"]) for s in steps)
|
|
91
|
+
c["anchored_writes"] += sum(1 for s in steps for o in s["ops"] if o["op"] == "W" and o["kind"] == "anchored")
|
|
92
|
+
c["opportunities"] += len(r.opps)
|
|
93
|
+
c["hazards"] += len(r.hazards)
|
|
94
|
+
c["hazard_trajectories"] += bool(r.hazards)
|
|
95
|
+
for h in r.hazards:
|
|
96
|
+
cls, why, ev = classify(h, t)
|
|
97
|
+
classes[model][cls] += 1
|
|
98
|
+
res = bool((d.get("info") or {}).get("resolved"))
|
|
99
|
+
c["resolved"] += res
|
|
100
|
+
resolved[model]["haz" if r.hazards else "nohaz"].append(res)
|
|
101
|
+
out = {}
|
|
102
|
+
tot = Counter()
|
|
103
|
+
for model, c in sorted(per_model.items()):
|
|
104
|
+
tot.update(c)
|
|
105
|
+
rv = resolved[model]
|
|
106
|
+
out[model] = dict(c, rate=round(c["hazards"] / c["opportunities"], 4) if c["opportunities"] else None,
|
|
107
|
+
ci=cp_ci(c["hazards"], c["opportunities"]), classes=dict(classes[model]),
|
|
108
|
+
resolved_with_hazard=[sum(rv["haz"]), len(rv["haz"])],
|
|
109
|
+
resolved_without_hazard=[sum(rv["nohaz"]), len(rv["nohaz"])],
|
|
110
|
+
coverage=dict(cov_all[model]))
|
|
111
|
+
print(f"{model:24s} traj {c['trajectories']:4d} steps {c['steps']:6d} zero-op {c['zero_op_steps'] / max(1, c['steps']):.2f} "
|
|
112
|
+
f"opp {c['opportunities']:5d} haz {c['hazards']:4d} rate {out[model]['rate']} {out[model]['ci']} "
|
|
113
|
+
f"cls {dict(classes[model])} res(haz) {out[model]['resolved_with_hazard']} res(no) {out[model]['resolved_without_hazard']}")
|
|
114
|
+
pool(out)
|
|
115
|
+
print("ALL", out["ALL"])
|
|
116
|
+
dump(out, "tarsur.json")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Configurations the dataset publishes twice: every trajectory of the first is
|
|
120
|
+
# an exact copy of the second's. Kept in the per-model rows, left out of ALL.
|
|
121
|
+
DUPLICATES = {"gpt-5.2-high": "gpt-5.2-codex"}
|
|
122
|
+
|
|
123
|
+
POOLED = ("trajectories", "steps", "zero_op_steps", "ops", "anchored_writes", "opportunities", "hazards",
|
|
124
|
+
"hazard_trajectories", "resolved")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def pool(out):
|
|
128
|
+
"""Pool the per-model rows into ALL, skipping published duplicates."""
|
|
129
|
+
tot, allcls = Counter(), Counter()
|
|
130
|
+
for model, o in out.items():
|
|
131
|
+
if model == "ALL":
|
|
132
|
+
continue
|
|
133
|
+
if model in DUPLICATES:
|
|
134
|
+
o["duplicate_of"] = DUPLICATES[model]
|
|
135
|
+
continue
|
|
136
|
+
tot.update({k: o.get(k, 0) for k in POOLED})
|
|
137
|
+
allcls.update(o.get("classes", {}))
|
|
138
|
+
out["ALL"] = dict(tot, rate=round(tot["hazards"] / tot["opportunities"], 4) if tot["opportunities"] else None,
|
|
139
|
+
ci=cp_ci(tot["hazards"], tot["opportunities"]), classes=dict(allcls),
|
|
140
|
+
models=sum(1 for k in out if k != "ALL" and k not in DUPLICATES),
|
|
141
|
+
excluded_duplicates=sorted(DUPLICATES))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
if sys.argv[1:] == ["--repool"]: # re-pool the saved per-model rows without the raw data
|
|
146
|
+
from common import WORK as _W
|
|
147
|
+
_out = json.load(open(os.path.join(_W, "tarsur.json")))
|
|
148
|
+
_out.pop("ALL", None)
|
|
149
|
+
pool(_out)
|
|
150
|
+
print("ALL", _out["ALL"])
|
|
151
|
+
dump(_out, "tarsur.json")
|
|
152
|
+
else:
|
|
153
|
+
main()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Harness package for Phase 2 intervention experiments."""
|
|
2
|
+
|
|
3
|
+
from .freshness_hook import FreshnessHook, ObservationBinding
|
|
4
|
+
from .arms import InterventionArm, apply_intervention_arm
|
|
5
|
+
from .miniswe_scaffold import MiniSWEScaffold
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"FreshnessHook",
|
|
9
|
+
"ObservationBinding",
|
|
10
|
+
"InterventionArm",
|
|
11
|
+
"apply_intervention_arm",
|
|
12
|
+
"MiniSWEScaffold",
|
|
13
|
+
]
|