#!/usr/bin/env python3 # Copyright 2026 SZL Holdings # SPDX-License-Identifier: Apache-2.0 # # check_tau_eval.py — CI guard for the szl-tau-tool-rules eval suite. # # Keeps the τ-bench-style tool-rule-following eval HONEST: # * the suite must contain >=1 NEGATIVE-CONTROL task (expect_refusal=True), # * a real run must score < 1.0 for an ALWAYS-PASS agent (proves the rules # actually reject bad trajectories — the score is non-trivial), and # * the reference rule-follower must pass every positive task and refuse every # negative control (proves the suite is satisfiable by a correct agent). # No fabricated numbers — the validator runs the REAL szl_tau_eval.run_suite(). # # --selftest feeds the validator degenerate runs and asserts it REJECTS each # before the guard trusts it (org guard pattern; cf. # eval-arena-negative-control.yml). Org policy: github-owned actions, SHAs pinned. import os import sys # allow running as `python3 scripts/check_tau_eval.py` from the repo root: the # eval module lives at the repo root next to serve.py. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) def validate_run(res: dict) -> tuple[bool, str]: """Assert a τ-bench run is structurally honest. Returns (ok, reason).""" if not isinstance(res, dict): return False, "result is not a dict" if not res.get("tasks"): return False, "no tasks in run" if res.get("as_of") is None: return False, "missing as_of (score must be dated)" if res.get("determinism_hash") is None: return False, "missing determinism_hash" neg = sum(1 for t in res["tasks"] if t.get("expect_refusal")) if neg < 1: return False, "no negative-control task (expect_refusal=True)" if res.get("pass_at_1") is None: return False, "missing pass_at_1" return True, "ok" def _always_pass_runner(scenario): # An agent that takes every disallowed action and never refuses. return {"trajectory": [{"tool": "issue_refund", "allowed": True}, {"tool": "send_email", "allowed": True}, {"tool": "run_sql", "allowed": True}, {"tool": "assign_seat", "allowed": True}, {"tool": "reset_password", "allowed": True}], "refused": False} def main(argv) -> int: import szl_tau_eval as tau if "--selftest" in argv: # degenerate runs the validator MUST reject bad = [ ({}, "empty"), ({"tasks": []}, "no tasks"), ({"tasks": [{"expect_refusal": False}], "as_of": "x", "determinism_hash": "y", "pass_at_1": 1.0}, "no negative control"), ({"tasks": [{"expect_refusal": True}], "determinism_hash": "y", "pass_at_1": 1.0}, "missing as_of"), ] for run, label in bad: ok, _ = validate_run(run) if ok: print("SELFTEST FAIL: validator accepted degenerate run: %s" % label) return 1 # a good run the validator MUST accept good = tau.run_suite() ok, why = validate_run(good) if not ok: print("SELFTEST FAIL: validator rejected a good run: %s" % why) return 1 print("SELFTEST OK: validator rejects degenerate runs, accepts a good one") return 0 # real run with the reference rule-follower ref = tau.run_suite() ok, why = validate_run(ref) if not ok: print("GUARD FAIL: reference run not honest: %s" % why) return 1 if ref["pass_at_1"] < 1.0: print("GUARD FAIL: reference rule-follower should pass all tasks, got %.4f" % ref["pass_at_1"]) return 1 # NON-TRIVIALITY: an always-pass agent must NOT get a perfect score ap = tau.run_suite(runner=_always_pass_runner) if ap["pass_at_1"] >= 1.0: print("GUARD FAIL: always-pass agent scored %.4f — suite is trivial!" % ap["pass_at_1"]) return 1 print("GUARD OK: suite=%s %s as_of=%s | reference pass^1=%.4f | " "always-pass pass^1=%.4f (< 1.0, score is non-trivial) | det=%s" % (ref["suite_id"], ref["suite_version"], ref["as_of"], ref["pass_at_1"], ap["pass_at_1"], ref["determinism_hash"])) return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))