SQL-Query-Env / training /eval_before_after.py
laterabhi's picture
Sync from GitHub: serving-only image deps, app_port, discoverability tags, buildable package
60dfa24 verified
Raw History Blame Contribute Delete
4.73 kB
"""
Before / after evaluation for README and judges.
"Before" = same structured suggestions as the fallback policy but an empty
optimized_query (no DuckDB comparison — analysis-only).
"After" = full deterministic fallback with real optimized SQL.
No API keys required.
Usage:
python training/eval_before_after.py --save-dir results
"""
from __future__ import annotations
import argparse
import json
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
from baseline_runner import FALLBACK_SOLUTIONS, TASK_IDS # noqa: E402
from graders import grade # noqa: E402
from models import Action # noqa: E402
from tasks import TASKS # noqa: E402
def _before_action(task_id: str) -> Action:
sol = FALLBACK_SOLUTIONS[task_id]
return Action(
suggestions=sol["suggestions"],
optimized_query="",
summary=sol["summary"],
estimated_improvement=sol["estimated_improvement"],
approved=sol["approved"],
)
def _after_action(task_id: str) -> Action:
sol = FALLBACK_SOLUTIONS[task_id]
return Action(
suggestions=sol["suggestions"],
optimized_query=sol["optimized_query"],
summary=sol["summary"],
estimated_improvement=sol["estimated_improvement"],
approved=sol["approved"],
)
def run_eval() -> dict:
rows = []
for task_id in TASK_IDS:
td = TASKS[task_id]
b = grade(td, _before_action(task_id))
a = grade(td, _after_action(task_id))
rows.append(
{
"task_id": task_id,
"task_name": td["task_name"],
"difficulty": td["difficulty"],
"before_score": b.score,
"after_score": a.score,
"delta": round(a.score - b.score, 4),
}
)
return {"rows": rows}
def write_table(path: str, data: dict) -> None:
lines = [
"# Before / after — execution-grounded reward",
"",
"| Task | Difficulty | Before (no SQL) | After (fallback) | Δ |",
"|------|------------|-----------------|------------------|---|",
]
for r in data["rows"]:
lines.append(
f"| {r['task_name'][:40]} | {r['difficulty']} | "
f"{r['before_score']:.4f} | {r['after_score']:.4f} | {r['delta']:+.4f} |"
)
b_avg = sum(r["before_score"] for r in data["rows"]) / len(data["rows"])
a_avg = sum(r["after_score"] for r in data["rows"]) / len(data["rows"])
lines += [
"",
f"**Mean before:** {b_avg:.4f} ",
f"**Mean after:** {a_avg:.4f} ",
f"**Mean Δ:** {a_avg - b_avg:+.4f}",
"",
"_Before = non-empty suggestions but `optimized_query` empty — no speedup/correctness signal._",
]
with open(path, "w", encoding="utf-8") as f:
f.write("\n".join(lines))
def write_chart(path: str, data: dict) -> None:
try:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
except ImportError:
print("[WARN] matplotlib not installed — skipping chart", flush=True)
return
labels = [r["task_id"].replace("task_", "") for r in data["rows"]]
before = [r["before_score"] for r in data["rows"]]
after = [r["after_score"] for r in data["rows"]]
x = range(len(labels))
w = 0.35
fig, ax = plt.subplots(figsize=(10, 5))
ax.bar([i - w / 2 for i in x], before, width=w, label="Before (no optimized SQL)")
ax.bar([i + w / 2 for i in x], after, width=w, label="After (fallback + DuckDB)")
ax.set_xticks(list(x))
ax.set_xticklabels(labels, rotation=25, ha="right")
ax.set_ylim(0, 1.0)
ax.set_ylabel("Reward")
ax.legend()
ax.set_title("Reward spread: analysis-only vs execution-grounded")
fig.tight_layout()
fig.savefig(path, dpi=150)
plt.close(fig)
print(f"[OK] Chart → {path}", flush=True)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument(
"--save-dir",
default="results",
help="Directory for before_after_table.md and JSON",
)
args = ap.parse_args()
save_dir = args.save_dir
os.makedirs(save_dir, exist_ok=True)
data = run_eval()
json_path = os.path.join(save_dir, "before_after_eval.json")
with open(json_path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
md_path = os.path.join(save_dir, "before_after_table.md")
write_table(md_path, data)
png_path = os.path.join(save_dir, "before_after_chart.png")
write_chart(png_path, data)
print(f"[OK] {json_path}", flush=True)
print(f"[OK] {md_path}", flush=True)
if __name__ == "__main__":
main()