selfheal-sql / src /selfheal_sql /gate_decision.py
HideIron's picture
readme + dash cleanup in source
898a901 verified
Raw History Blame Contribute Delete
4.62 kB
"""Deployment gate: decide whether the retrained adapter ships, using AgentTune's
DeploymentGate verbatim (vendored at third_party/agenttune_gate, commit 1dfd054).
The gate is model-agnostic by design: `model_runner_fn(input_text) -> verdict` is
supplied by the caller and compared against `expected_verdict` per case ("keeps the
gate model-agnostic and testable without a GPU" - their docstring). Our runners look
up each gate question's execution-checker outcome from the student harness's logged
runs, so the verdict IS Spider execution accuracy.
Usage:
python gate_decision.py --old baseline-gate --new healed-gate --out ../../data/gate
"""
from __future__ import annotations
import argparse
import importlib.util
import json
import os
import sys
import types
from dataclasses import asdict
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
RUNS_REPO = "HideIron/selfheal-sql-runs"
def load_gate_module():
"""Import the vendored deployment_gate.py without installing agenttune
(its only non-stdlib import is the PASS_VERDICTS constant, stubbed here 1:1)."""
pkg_names = ["agenttune", "agenttune.decide"]
for n in pkg_names:
if n not in sys.modules:
m = types.ModuleType(n)
m.__path__ = [] # mark as package
sys.modules[n] = m
st = types.ModuleType("agenttune.decide.state")
st.PASS_VERDICTS = frozenset({"PASS", "APPROVE", "COMPLETE"}) # verbatim
sys.modules["agenttune.decide.state"] = st
path = os.path.join(ROOT, "third_party", "agenttune_gate", "deployment_gate.py")
spec = importlib.util.spec_from_file_location("deployment_gate", path)
mod = importlib.util.module_from_spec(spec)
sys.modules["deployment_gate"] = mod # dataclasses resolves cls.__module__ here
spec.loader.exec_module(mod)
return mod
def results_lookup(run_name: str) -> dict[str, str]:
from huggingface_hub import hf_hub_download
p = hf_hub_download(RUNS_REPO, f"runs/{run_name}/results.jsonl", repo_type="dataset")
out = {}
for line in open(p, encoding="utf-8"):
r = json.loads(line)
out[r["qid"]] = "CORRECT" if r["status"] == "correct" else r["status"].upper()
return out
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--old", default="baseline-gate")
ap.add_argument("--new", default="healed-gate")
ap.add_argument("--out", default=os.path.join(ROOT, "data", "gate"))
ap.add_argument("--task-regression-tol", type=float, default=0.0)
ap.add_argument("--no-push", action="store_true")
args = ap.parse_args()
gate_mod = load_gate_module()
old_v, new_v = results_lookup(args.old), results_lookup(args.new)
qids = sorted(old_v)
assert set(qids) == set(new_v), "old/new runs cover different questions"
test_set = [{"pipeline_id": q, "input_text": q, "expected_verdict": "CORRECT"}
for q in qids]
gate = gate_mod.DeploymentGate()
decision = gate.evaluate_decision(
test_set=test_set,
old_model_fn=lambda qid: old_v[qid],
new_model_fn=lambda qid: new_v[qid],
task_regression_tol=args.task_regression_tol,
)
comparison = gate.compare_models(
test_set=test_set,
old_model_fn=lambda qid: old_v[qid],
new_model_fn=lambda qid: new_v[qid],
)
out = {
"gate_decision": asdict(decision),
"ab_comparison": asdict(comparison),
"old_run": args.old, "new_run": args.new,
"per_question_flips": {
"fixed": [q for q in qids if old_v[q] != "CORRECT" and new_v[q] == "CORRECT"],
"broken": [q for q in qids if old_v[q] == "CORRECT" and new_v[q] != "CORRECT"],
},
"gate_source": "AgentTune deployment_gate.py @ 1dfd054 (vendored verbatim)",
}
os.makedirs(args.out, exist_ok=True)
with open(os.path.join(args.out, "gate_decision.json"), "w", encoding="utf-8") as f:
json.dump(out, f, indent=2)
print(json.dumps({k: out[k] for k in ("gate_decision", "ab_comparison")}, indent=2))
print("fixed:", len(out["per_question_flips"]["fixed"]),
"broken:", len(out["per_question_flips"]["broken"]))
if not args.no_push:
from huggingface_hub import HfApi
HfApi().upload_folder(repo_id=RUNS_REPO, repo_type="dataset", folder_path=args.out,
path_in_repo="gate", commit_message="gate decision")
if __name__ == "__main__":
main()