auditable 0.3.0__tar.gz → 0.4.0__tar.gz
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.
- auditable-0.4.0/CHANGELOG.md +42 -0
- {auditable-0.3.0 → auditable-0.4.0}/PKG-INFO +126 -14
- {auditable-0.3.0 → auditable-0.4.0}/README.md +119 -13
- {auditable-0.3.0 → auditable-0.4.0}/examples/README.md +30 -0
- auditable-0.4.0/examples/example_add_your_method.py +68 -0
- auditable-0.4.0/examples/example_leaderboard.py +41 -0
- auditable-0.4.0/examples/leaderboard.ipynb +275 -0
- {auditable-0.3.0 → auditable-0.4.0}/pyproject.toml +5 -1
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/__init__.py +1 -1
- auditable-0.4.0/src/auditable/bench/__init__.py +16 -0
- auditable-0.4.0/src/auditable/bench/__main__.py +66 -0
- auditable-0.4.0/src/auditable/bench/_core.py +417 -0
- auditable-0.4.0/src/auditable/bench/corpora/__init__.py +8 -0
- auditable-0.4.0/src/auditable/bench/corpora/pre.py +73 -0
- auditable-0.4.0/src/auditable/bench/corpora/swegym.py +55 -0
- auditable-0.4.0/src/auditable/bench/corpora/tau.py +92 -0
- auditable-0.4.0/src/auditable/bench/corpora/whoandwhen.py +94 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/README.md +44 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/legacy_run_keys.json +131 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__claude-opus-4.8.json +1869 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__deepseek-r1.json +1839 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__gemini.json +1723 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__gemma-3-12b.json +1758 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__gpt-5.4.json +1869 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__gpt-5.5.json +1869 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__gpt-oss-20b.json +1741 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__llama-3.3-70b.json +1869 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__mistral-small.json +1602 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__nova-micro.json +1439 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__all_at_once__qwen3-32b.json +1854 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__claude-opus-4.8.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__deepseek-r1.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__gemini.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__gemma-3-12b.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__gpt-5.4.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__gpt-5.5.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__llama-3.3-70b.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__mistral-small.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__nova-micro.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__binary_search__qwen3-32b.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__claude-opus-4.8.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__deepseek-r1.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__gemini.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__gemma-3-12b.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__gpt-5.4.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__gpt-5.5.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__llama-3.3-70b.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__mistral-small.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__nova-micro.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/llm_judge/whoandwhen__step_by_step__qwen3-32b.json +896 -0
- auditable-0.4.0/src/auditable/bench/data/pre/README.md +17 -0
- auditable-0.4.0/src/auditable/bench/data/pre/llm_judge_method/llama-3.3-70b.json +4757 -0
- auditable-0.4.0/src/auditable/bench/golden/detect__swegym.json +100 -0
- auditable-0.4.0/src/auditable/bench/golden/detect__tau.json +105 -0
- auditable-0.4.0/src/auditable/bench/golden/live__swegym.json +80 -0
- auditable-0.4.0/src/auditable/bench/golden/live__tau.json +80 -0
- auditable-0.4.0/src/auditable/bench/golden/localize__whoandwhen.json +42 -0
- auditable-0.4.0/src/auditable/bench/golden/localize__whoandwhen__llm.json +197 -0
- auditable-0.4.0/src/auditable/bench/golden/pre__over_privilege.json +157 -0
- auditable-0.4.0/src/auditable/bench/llm_rows.py +421 -0
- auditable-0.4.0/src/auditable/bench/protocols.py +118 -0
- auditable-0.4.0/src/auditable/bench/protocols_config.py +76 -0
- auditable-0.4.0/src/auditable/bench/protocols_live.py +109 -0
- auditable-0.4.0/src/auditable/bench/protocols_step.py +133 -0
- auditable-0.4.0/src/auditable/bench/tasks/__init__.py +5 -0
- auditable-0.4.0/src/auditable/bench/tasks/detect.py +64 -0
- auditable-0.4.0/src/auditable/bench/tasks/live.py +112 -0
- auditable-0.4.0/src/auditable/bench/tasks/localize.py +112 -0
- auditable-0.4.0/src/auditable/bench/tasks/pre.py +219 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/run/supervised.py +46 -0
- auditable-0.4.0/tests/test_bench_core.py +284 -0
- auditable-0.4.0/tests/test_bench_extras.py +25 -0
- auditable-0.4.0/tests/test_bench_golden_coverage.py +174 -0
- auditable-0.4.0/tests/test_bench_live.py +137 -0
- auditable-0.4.0/tests/test_bench_llm_rows.py +203 -0
- auditable-0.4.0/tests/test_bench_localize.py +185 -0
- auditable-0.4.0/tests/test_bench_pre.py +325 -0
- auditable-0.4.0/tests/test_bench_protocols.py +203 -0
- auditable-0.4.0/tests/test_bench_swegym_selection.py +132 -0
- auditable-0.4.0/tests/test_bench_tau.py +109 -0
- auditable-0.4.0/tests/test_methods_spline_size.py +46 -0
- auditable-0.3.0/CHANGELOG.md +0 -28
- {auditable-0.3.0 → auditable-0.4.0}/.gitignore +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/LICENSE +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_agentpile.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_audit_report.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_end_to_end.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_langchain_capture.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_langgraph_capture.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_langgraph_live_replay.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_langgraph_llm_agent.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_live_monitor.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_live_replay.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_mcp_capture.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_methods.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_payment_audit.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_post_rank_run.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_pre_lint_plan.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_standalone_report.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_state_anomaly.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/examples/example_touch_capture.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/_report_figures.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/analysis.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/audit_report.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/base.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/chain.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/compound.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/data/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/data/inject.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/datasets.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/_grade_projection.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/agentpile.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/pyg.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/spec.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/step_deps.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/trace.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/featurize/who_when.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/adapters/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/adapters/declared_plan.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/adapters/own_record.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/adapters/protocol.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/adapters/tau_bench.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/features.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/grounding.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/loader.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/pre.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/risk.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/session.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/graph/touch.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/harness/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/integrations/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/integrations/langchain.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/integrations/langgraph.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/integrations/mcp.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/llm/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/llm/backend.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/llm/cache.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/llm/who_when.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/_pygod.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/config/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/config/llm.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/config/rules.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/online/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/online/rules.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/run/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/run/baselines.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/run/graph.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/run/pyod.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/step/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/step/graph.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/step/llm.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/step/rules.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/methods/step/supervised.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/model/__init__.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/record.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/src/auditable/report.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/fixtures/agentpile_grade/README.md +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/fixtures/agentpile_grade/agent_reward_bench_records.json +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/fixtures/agentpile_grade/b_openhands_multi_call_turn.json +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/fixtures/agentpile_grade/swe_agent_synthetic.json +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/fixtures/grade_3839a57_features.json +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_adapters.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_agentpile_adapter.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_analysis.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_audit_report.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_auditors.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_basic.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_compound.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_data_inject.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_data_module.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_datasets.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_featurize.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_gate.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_graph.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_graph_execution_reach.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_graph_features.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_graph_loader.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_graph_risk.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_grounding.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_langchain_integration.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_langgraph_integration.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_langgraph_replay_bridge.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_live_session.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_llm.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_mcp_integration.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_methods_config.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_methods_graph.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_methods_pygod_step.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_methods_registry.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_methods_run_tabular.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_methods_slice.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_methods_step_rules.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_parity_consumption.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_pre.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_pre_coverage.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_public_api.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_replay.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_report.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_session_graph.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_session_graph_projection.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_sink.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_touch.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_touch_replay_bridge.py +0 -0
- {auditable-0.3.0 → auditable-0.4.0}/tests/test_version.py +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.0 (2026-11-14)
|
|
4
|
+
|
|
5
|
+
0.4.0 adds leaderboards: `auditable.bench` reruns the tables of the CatchBench board on the same populations and protocols, and places any new method on them.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `auditable.bench`: `leaderboard(task_id, methods=None, extra=())` scores methods on a task and returns a ranked `Board` (`print`, `to_markdown()`, `to_dict()`, `compare_golden()`, `rank_of()`); `extra=[MyMethod]` adds a method that is not in the registry. A method whose extra is missing is listed as skipped with the extra named, and a method that raises is listed as an error while the rest of the board is still scored. Oracle rows, which read the labels, are shown as references and never ranked; control rows are ranked and marked.
|
|
10
|
+
- Six tasks, each reproducing a table of the CatchBench 0.1.3 board row by row: `detect/swegym` (376 runs) and `detect/tau` (660 runs), run-level failure detection by ROC-AUC; `live/swegym` and `live/tau`, early warning on 25, 50, 75 and 100 percent prefixes; `localize/whoandwhen` (126 failed runs), decisive-step localization by top-1, top-3 and MRR, including 31 LLM-judge rows replayed from CatchBench's committed prediction caches without an API call; and `pre/over_privilege` (1,187 configurations), configuration audit by F1, precision and recall, overall and per source. The populations are read through agentpile at the dataset revisions CatchBench pins, with the selection rules of GRADE commit 3839a57; the protocols are ported from CatchBench's adapters.
|
|
11
|
+
- `run.lr_spline_flat`, the flexible size control: a cubic spline of the four GRADE `flat` counts, then the board's logistic regression, ported from the `size (spline)` arm of CatchBench's size audits. It joins the `detect` and `live` boards as a control. On `detect/swegym` it ranks second, above both GRADE dependency regressions; on `detect/tau` it ranks below `run.lr_flatdep`. The library now holds 44 methods.
|
|
12
|
+
- Golden files with the board values of every row and the SHA-256 of the board they come from; the spline control's value comes from CatchBench's committed size audits instead, with a 0.001 tolerance. `python -m auditable.bench <task> --golden` compares a run with them and fails on a mismatch, on an error row, on a golden row left unchecked (skipped, missing a metric, absent from the task's default rows, or in a golden file that is missing), and on a run that compared no golden value; `--allow-skipped` accepts rows skipped for a missing extra. Three rows differ on macOS arm64 by a known platform effect and are reported as platform values; `--exact` counts them as mismatches, and the weekly Linux run uses it.
|
|
13
|
+
- `python -m auditable.bench`, the command-line entry: lists the tasks, prints a board, or emits it as Markdown or JSON.
|
|
14
|
+
- The `bench` extra (`pyod`, `scikit-learn`, `numpy`, `networkx`, `agentpile[hf]`); the graph rows also need `graph-ad`.
|
|
15
|
+
- CI: a `bench-core` job runs the bench unit tests, which download nothing, a weekly `bench golden` workflow reruns every task on Linux against its golden file, and a weekly `bench platforms` workflow installs the package from a clean checkout on Linux, macOS, and Windows (Python 3.10 and 3.13) with the README's graph-ad steps and checks three tasks against their golden files.
|
|
16
|
+
|
|
17
|
+
## 0.3.0 (2026-10-25)
|
|
18
|
+
|
|
19
|
+
0.3.0 turns `auditable` into a library of auditing methods for AI agents, with one interface per family and the ability to read public agent traces through agentpile.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- `auditable.methods`: 43 methods in four families, each with one interface. The `config` family (8 methods) audits an agent configuration before a run, `online` (5) raises causal alarms while a run streams, and `run` (16) and `step` (14) detect a failed run and localize its decisive step afterwards. A registry (`list_methods`, `get_method`) returns each method's `MethodCard`, which records its fit scope, fidelity to its source, required fields, extras and citations. The library holds baselines from the literature (PyOD and PyGOD detectors, GUARDIAN-Lite, G-Safeguard-Lite, the Who&When LLM-judge protocols, OWASP-style configuration rules) beside the methods introduced with GRADE and CatchBench; every method that reproduces a CatchBench 0.1.2 board row matches it sample by sample.
|
|
24
|
+
- `auditable.featurize`: trace normalization for step lists, run dicts, typed graphs, `SessionGraph` and `TraceView` inputs, and the features the methods compute (the GRADE structural layers, the Who&When step matrix, the step dependency graph, PyG encodings).
|
|
25
|
+
- `auditable.llm`: a `prompt -> reply` backend protocol, `FixedResponseBackend` for tests and replays, `OpenAICompatibleBackend` (standard library only), and a prediction cache.
|
|
26
|
+
- `auditable.from_agentpile`, `configs_from_agentpile`, `step_targets`, `run_targets` and `auditable.datasets.load_traces` read agentpile records (public datasets, trace files, captured runs) into the methods' inputs and labels. `convention="grade"` with `deps="grade"` reproduces the steps, dependencies and graphs of GRADE's converters (commit 3839a57) on seven corpora; `convention="generic"` (the default) makes one step per message and one per tool call.
|
|
27
|
+
- Extras `tabular-ad`, `graph-ad` and `agentpile`; the core install still has no third-party dependency.
|
|
28
|
+
- `docs/methods-contract.md`, the contract a method follows and the behaviours a pinned consumer such as CatchBench relies on.
|
|
29
|
+
|
|
30
|
+
### Behaviour of the Conversion
|
|
31
|
+
|
|
32
|
+
- In `convention="generic"`, content given as a list of parts (dicts with a string `type`, possibly mixed with bare strings) keeps its text and writes every other part (an image, a document) as `[<type>]`, dropping that part's other fields; `meta["report"]` counts those parts and says how each step of the record maps into the trace.
|
|
33
|
+
- `convention="grade"` raises `ValueError` for a run from a dataset revision other than the one its projection was checked on.
|
|
34
|
+
- A run with subagent trajectories is refused by default; the error names the two ways to read it, `from_agentpile(run.subagents, ...)` and `subagents="skip"`.
|
|
35
|
+
|
|
36
|
+
### Deprecated
|
|
37
|
+
|
|
38
|
+
- `ActionGate`, `ReferenceLedger` and `DataAuditor` emit a `DeprecationWarning` when constructed and will be removed in a later release.
|
|
39
|
+
|
|
40
|
+
## Earlier Releases
|
|
41
|
+
|
|
42
|
+
Releases up to 0.2.1 are listed on [PyPI](https://pypi.org/project/auditable/#history) and as [GitHub tags](https://github.com/yzhao062/auditable/tags).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: auditable
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A library of auditing methods for AI agents: configuration audits, run-level failure detection, step-level fault localization, and online monitors, behind one API per family.
|
|
5
5
|
Project-URL: Homepage, https://github.com/yzhao062/auditable
|
|
6
6
|
Project-URL: Repository, https://github.com/yzhao062/auditable
|
|
@@ -27,6 +27,12 @@ Requires-Dist: agentpile[hf]<0.4,>=0.3; extra == 'agentpile'
|
|
|
27
27
|
Requires-Dist: numpy>=1.21; extra == 'agentpile'
|
|
28
28
|
Provides-Extra: anomaly
|
|
29
29
|
Requires-Dist: pyod>=1.1.0; extra == 'anomaly'
|
|
30
|
+
Provides-Extra: bench
|
|
31
|
+
Requires-Dist: agentpile[hf]<0.4,>=0.3; extra == 'bench'
|
|
32
|
+
Requires-Dist: networkx>=3.0; extra == 'bench'
|
|
33
|
+
Requires-Dist: numpy>=1.21; extra == 'bench'
|
|
34
|
+
Requires-Dist: pyod>=1.1.0; extra == 'bench'
|
|
35
|
+
Requires-Dist: scikit-learn>=1.0; extra == 'bench'
|
|
30
36
|
Provides-Extra: dev
|
|
31
37
|
Requires-Dist: build>=1.0; extra == 'dev'
|
|
32
38
|
Requires-Dist: pyod>=1.1.0; extra == 'dev'
|
|
@@ -73,20 +79,114 @@ Description-Content-Type: text/markdown
|
|
|
73
79
|
|
|
74
80
|
# auditable
|
|
75
81
|
|
|
76
|
-
**A library of auditing methods for AI agents: configuration audits before a run, online monitors while it runs, and failure detection and fault localization after it
|
|
82
|
+
**A library of auditing methods for AI agents, with a leaderboard for each task: configuration audits before a run, online monitors while it runs, and failure detection and fault localization after it.**
|
|
77
83
|
|
|
78
84
|
[](https://pypi.org/project/auditable/)
|
|
79
85
|
[](https://pypi.org/project/auditable/)
|
|
80
86
|
[](https://github.com/yzhao062/auditable/blob/main/LICENSE)
|
|
81
87
|
[](https://github.com/yzhao062/auditable/actions/workflows/ci.yml)
|
|
88
|
+
[](https://colab.research.google.com/github/yzhao062/auditable/blob/main/examples/leaderboard.ipynb)
|
|
82
89
|
|
|
83
|
-
[Quick Start](#quick-start) · [Real Agent Traces](#real-agent-traces) · [Methods](#methods) · [Install](#install) · [
|
|
90
|
+
[Leaderboard](#leaderboard) · [Put Your Method on the Board](#put-your-method-on-the-board) · [Tasks](#tasks) · [Quick Start](#quick-start) · [Real Agent Traces](#real-agent-traces) · [Methods](#methods) · [Install](#install) · [Adding a Method](#adding-a-method) · [Citation](#citation)
|
|
84
91
|
|
|
85
92
|
</div>
|
|
86
93
|
|
|
94
|
+
`auditable` is a library of auditing methods for AI agents with a leaderboard for each task: one command reruns the [CatchBench](https://github.com/yzhao062/catchbench) board on your machine, and a method you write gets a rank on the same population and protocol.
|
|
95
|
+
|
|
96
|
+
## Leaderboard
|
|
97
|
+
|
|
98
|
+
Did the run fail? The board below ranks 17 methods by ROC-AUC on 376 SWE-Gym OpenHands runs, 188 of which failed. One command reruns it on the population and protocol of the CatchBench board:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install "auditable[bench]"
|
|
102
|
+
python -m auditable.bench detect/swegym
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
| Rank | Method | Algorithm | ROC-AUC |
|
|
106
|
+
|---|---|---|---|
|
|
107
|
+
| 1 | `run.pygod_gaan` | GAAN adversarial detector (PyGOD) | 0.850 |
|
|
108
|
+
| 2 | `run.lr_spline_flat` | Logistic regression on a cubic spline of run size, a control | 0.832 |
|
|
109
|
+
| 3 | `run.gsafeguard_lite` | G-Safeguard-Lite (supervised graph classifier) | 0.828 |
|
|
110
|
+
| 4 | `run.lr_full` | Logistic regression on all raw layers (GRADE full) | 0.819 |
|
|
111
|
+
| 5 | `run.lr_flatdep` | Logistic regression on size and dependency (GRADE flatdep) | 0.804 |
|
|
112
|
+
| 6 | `run.guardian_lite` | GUARDIAN-Lite (attribute-reconstruction autoencoder) | 0.767 |
|
|
113
|
+
| 7 | `run.pyod_ecod` | ECOD on run size | 0.765 |
|
|
114
|
+
| 8 | `run.pygod_conad` | CONAD contrastive graph autoencoder (PyGOD) | 0.750 |
|
|
115
|
+
| 9 | `run.lr_flat` | Logistic regression on run size (GRADE flat), the size baseline | 0.663 |
|
|
116
|
+
| 10 | `run.pyod_copod` | COPOD on run size | 0.625 |
|
|
117
|
+
| 11 | `run.pygod_anomalydae` | AnomalyDAE dual autoencoder (PyGOD) | 0.592 |
|
|
118
|
+
| 12 | `run.pyod_lof` | Local outlier factor on run size | 0.584 |
|
|
119
|
+
| 13 | `run.pyod_iforest` | Isolation Forest on run size | 0.571 |
|
|
120
|
+
| 14 | `run.pygod_dominant` | DOMINANT graph autoencoder (PyGOD) | 0.547 |
|
|
121
|
+
| 15 | `run.random` | Random run scores, a control | 0.483 |
|
|
122
|
+
| 16 | `run.pyod_knn` | KNN distance on run size | 0.446 |
|
|
123
|
+
| 17 | `run.pyod_hbos` | HBOS on run size | 0.319 |
|
|
124
|
+
|
|
125
|
+
PyGOD's GAAN, a literature baseline, leads this board at 0.850. Second, at 0.832, is the flexible size control `run.lr_spline_flat`: a cubic spline of the same four run-size counts the linear size baseline `run.lr_flat` reads (0.663). Run size alone, fitted without a linear restriction, ranks above G-Safeguard-Lite and above both GRADE dependency regressions (0.819 and 0.804), so this board does not show that dependency structure predicts failure beyond size, the reading GRADE retired on 2026-09-08. Read a new row against both size controls; on tau-bench the order changes, and `run.lr_flatdep` (0.665) ranks above the spline control (0.631).
|
|
126
|
+
|
|
127
|
+
The 16 CatchBench rows equal the CatchBench board, which was computed on Linux; the spline control is not a board row, and its reference value is the `size (spline)` arm of CatchBench's committed size audit. On macOS, `run.pyod_lof` prints 0.582 instead of 0.584, and `--golden` reports that as a platform value and not as a mismatch. The first run downloads the data through agentpile; later runs load SWE-Gym from the cache in about 3 seconds, and the full board takes about 70 seconds on a laptop CPU, of which PyGOD GAAN is the slowest row at about 37 seconds. The graph rows need `auditable[graph-ad]` and the PyG sampling backend ([Install](#install)); without them those rows are listed as skipped with the extra named, and the rest of the board is still scored.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
python -m auditable.bench # list the tasks
|
|
131
|
+
python -m auditable.bench detect/swegym --methods run.lr_flat run.pyod_ecod # score chosen rows only
|
|
132
|
+
python -m auditable.bench detect/swegym --golden # exit 1 if a row differs from the CatchBench board
|
|
133
|
+
python -m auditable.bench detect/swegym --markdown # a Markdown table; --json prints JSON
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Put Your Method on the Board
|
|
137
|
+
|
|
138
|
+
A method is a class with a `MethodCard`. Pass it as `extra=` and `leaderboard` scores it beside the default rows, under the protocol of its kind: a supervised detector gets the mean ROC-AUC over five seeds of stratified 5-fold cross-validation with a fresh estimator per fold, and an unsupervised one gets a single `fit_score` over the whole population. The snippet below adds a random forest on run size, a deliberately plain method:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
142
|
+
|
|
143
|
+
from auditable.base import BaseRunDetector, MethodCard
|
|
144
|
+
from auditable.bench import leaderboard
|
|
145
|
+
from auditable.featurize import grade_layer_matrix
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class ForestOnSize(BaseRunDetector):
|
|
149
|
+
card = MethodCard(method_id="run.forest_flat", name="Random forest on run size", family="run",
|
|
150
|
+
fit_scope="inductive", supervised=True, output_kind="score", fidelity="original")
|
|
151
|
+
|
|
152
|
+
def fit(self, runs, y=None):
|
|
153
|
+
self.model_ = RandomForestClassifier(200, random_state=0)
|
|
154
|
+
self.model_.fit(grade_layer_matrix(list(runs), "flat"), y)
|
|
155
|
+
self.decision_scores_ = self.decision_function(runs)
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
def decision_function(self, runs):
|
|
159
|
+
return self.model_.predict_proba(grade_layer_matrix(list(runs), "flat"))[:, 1]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
board = leaderboard("detect/swegym", extra=[ForestOnSize])
|
|
163
|
+
print(board.rank_of("run.forest_flat"), board["run.forest_flat"].scores)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The last line prints the rank of the new row among all rows and its scores. `print(board)` shows the full table, with the CatchBench value in brackets beside each row that has one, and `board.to_markdown()`, `board.to_dict()` and `board.compare_golden()` export it. `methods=[...]` scores only the rows you name, which is quick while you iterate (`leaderboard("detect/swegym", methods=["run.lr_flat", ForestOnSize])`), and `Entry(method, kwargs={...}, label="...", key="...")` adds a row for a variant with constructor arguments (give each variant its own key, because a row that keeps the method id is compared with that method's CatchBench value). A method that raises is listed as an error and the rest of the board is still scored. The [method contract](https://github.com/yzhao062/auditable/blob/main/docs/methods-contract.md) lists the conventions for the four families.
|
|
167
|
+
|
|
168
|
+
## Tasks
|
|
169
|
+
|
|
170
|
+
Six tasks ship with 0.4.0. Each fixes a population and a protocol, so every method on a board is scored on the same data in the same way. `detect` scores the run detectors, `localize` the step locators, `live` the online monitors (run detectors also take part, scored on prefixes of the run), and `pre` the configuration auditors.
|
|
171
|
+
|
|
172
|
+
| Task | Question | Population | Metric |
|
|
173
|
+
|---|---|---|---|
|
|
174
|
+
| `detect/swegym` | Did the run fail? | SWE-Gym OpenHands trajectories, one scaffold: 376 runs, 188 failed | ROC-AUC |
|
|
175
|
+
| `detect/tau` | Did the run fail? | tau-bench: 660 runs, 363 failed | ROC-AUC |
|
|
176
|
+
| `localize/whoandwhen` | Which step was the decisive mistake? | Who&When: 126 failed multi-agent runs with a human mistake step | top-1, top-3, MRR (mean reciprocal rank) |
|
|
177
|
+
| `live/swegym` | Can the failure be caught early? | SWE-Gym runs, read as prefixes of 25, 50, 75 and 100 percent of their steps | mean prefix ROC-AUC |
|
|
178
|
+
| `live/tau` | Can the failure be caught early? | tau-bench runs, read as prefixes of 25, 50, 75 and 100 percent of their steps | mean prefix ROC-AUC |
|
|
179
|
+
| `pre/over_privilege` | Is the agent configuration over-privileged before it runs? | 1,187 configurations from six sources | F1, precision, recall |
|
|
180
|
+
|
|
181
|
+
Every task id works with the commands above: `python -m auditable.bench <task>` reruns the board, and `leaderboard("<task>", extra=[...])` places a method on it. The LLM judges on `localize/whoandwhen` and `pre/over_privilege` are replayed from committed caches, so those boards need no API call.
|
|
182
|
+
|
|
183
|
+
The CatchBench board shows how the other tasks read. On Who&When localization, the best LLM judges (all-at-once) reach top-1 of about 0.42 to 0.45 against 0.119 for random, and the structural blast rule ties the position prior at 0.159 top-1. The tau-bench detection board is led by `run.lr_flatdep` and `run.lr_full`, tied at 0.665, against 0.619 for the size baseline. For over-privilege, the best rule scanner `config.owasp_asi_combined` reaches F1 0.654, flagging every configuration reaches 0.601, and an LLM judge reaches 0.695.
|
|
184
|
+
|
|
185
|
+
## What Is in the Library
|
|
186
|
+
|
|
87
187
|
`auditable` collects methods that audit LLM agents at three points: an agent configuration before it runs (PRE), a run while it streams (LIVE), and a finished run (POST). Methods are grouped into four families, and each family has one interface, in the way PyOD gives outlier detectors one interface. The library holds baselines from the literature (PyOD and PyGOD detectors, GUARDIAN, G-Safeguard, the Who&When LLM-judge protocols, OWASP-style configuration rules) beside the methods introduced with GRADE and CatchBench.
|
|
88
188
|
|
|
89
|
-
[CatchBench](https://github.com/yzhao062/catchbench) is the benchmark
|
|
189
|
+
[CatchBench](https://github.com/yzhao062/catchbench) is the paper and benchmark whose board the leaderboards reproduce: `auditable.bench` carries its populations and evaluation protocols, and every method here that appears on its board reproduces its row sample by sample ([Reproducing CatchBench](#reproducing-catchbench)). [agentpile](https://github.com/yzhao062/agentpile) is the converter that reads public agent-trace datasets into the one record the methods consume ([Real Agent Traces](#real-agent-traces)).
|
|
90
190
|
|
|
91
191
|
## Quick Start
|
|
92
192
|
|
|
@@ -211,6 +311,7 @@ Configuration rules and the step-by-step and binary-search LLM judges run on the
|
|
|
211
311
|
| Supervised | `run.lr_flat` | Logistic regression on run size (GRADE flat) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
212
312
|
| Supervised | `run.lr_flatdep` | Logistic regression on size and dependency (GRADE flatdep) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
213
313
|
| Supervised | `run.lr_full` | Logistic regression on all raw layers (GRADE full) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
314
|
+
| Supervised | `run.lr_spline_flat` | Logistic regression on a cubic spline of run size (flexible size control) | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
214
315
|
| PyOD detector | `run.pyod_copod` | COPOD on run size | [COPOD (Li et al., 2020)](https://arxiv.org/abs/2009.09463) |
|
|
215
316
|
| PyOD detector | `run.pyod_ecod` | ECOD on run size | [ECOD (Li et al., 2022)](https://arxiv.org/abs/2201.00382) |
|
|
216
317
|
| PyOD detector | `run.pyod_hbos` | HBOS on run size | HBOS (Goldstein and Dengel, 2012) |
|
|
@@ -253,15 +354,16 @@ Configuration rules and the step-by-step and binary-search LLM judges run on the
|
|
|
253
354
|
|
|
254
355
|
```bash
|
|
255
356
|
pip install auditable # core: the registry and the configuration rules, no third-party dependencies
|
|
357
|
+
pip install "auditable[bench]" # the leaderboards: PyOD, scikit-learn, NumPy, NetworkX, and agentpile with its Hugging Face loaders
|
|
256
358
|
pip install "auditable[tabular-ad]" # run, step, and online methods on trace features (NumPy, scikit-learn, PyOD, NetworkX)
|
|
257
359
|
pip install "auditable[agentpile]" # read public datasets, trace files and captured runs through agentpile
|
|
258
360
|
```
|
|
259
361
|
|
|
260
|
-
The examples, tests and benchmark harnesses are in the repository, not the wheel; to run them, install from a clone with `pip install -e ".[dev,tabular-ad]"`.
|
|
362
|
+
The examples, tests and benchmark harnesses are in the repository, not the wheel; to run them, install from a clone with `pip install -e ".[dev,tabular-ad,bench]"`.
|
|
261
363
|
|
|
262
364
|
The LLM judges take any `prompt -> reply` function as their backend; `OpenAICompatibleBackend` reaches an OpenAI-compatible endpoint with the standard library alone.
|
|
263
365
|
|
|
264
|
-
The graph methods (PyGOD detectors, GUARDIAN-Lite, G-Safeguard-Lite) need torch, PyTorch Geometric, PyGOD, and a sampling backend built for the installed torch, which pip cannot resolve from the extra. Install torch and the matching `pyg-lib` wheel first:
|
|
366
|
+
The graph methods (PyGOD detectors, GUARDIAN-Lite, G-Safeguard-Lite), and so the graph rows of the leaderboards, need torch, PyTorch Geometric, PyGOD, and a sampling backend built for the installed torch, which pip cannot resolve from the extra; they need Python 3.10 or newer, since torch 2.12.1 and `pyg-lib` publish no wheels for 3.9. Install torch and the matching `pyg-lib` wheel first (the same steps on Linux, macOS on Apple silicon, and Windows):
|
|
265
367
|
|
|
266
368
|
```bash
|
|
267
369
|
pip install "torch==2.12.1" --index-url https://download.pytorch.org/whl/cpu
|
|
@@ -270,7 +372,7 @@ pip install "auditable[graph-ad]"
|
|
|
270
372
|
python -c "import torch_geometric.typing as t; assert t.WITH_PYG_LIB"
|
|
271
373
|
```
|
|
272
374
|
|
|
273
|
-
Where torch is already installed, as on Colab, keep it: skip the first line, and in the `pyg-lib` URL replace `2.12.1+cpu` with the installed version, which `python -c "import torch; print(torch.__version__)"` prints
|
|
375
|
+
Where torch is already installed, as on Colab, keep it: skip the first line, and in the `pyg-lib` URL replace `2.12.1+cpu` with the installed version, which `python -c "import torch; print(torch.__version__)"` prints; the pinned CPU build would replace Colab's torch and break its preinstalled torchvision. PyG does not publish wheels for every patch release, so when pip finds no `pyg_lib` at that URL, use the `.0` release with the same CUDA tag: Colab's torch `2.14.1+cu130` takes `https://data.pyg.org/whl/torch-2.14.0+cu130.html`. The graph-ad install was checked for 0.4.0 on a Colab T4 runtime with its torch 2.14.1+cu130, and for 0.3.0 on Colab CPU and T4 runtimes with Colab's torch and on Linux aarch64 (DGX Spark) with the pinned one; a weekly workflow (`bench-platforms.yml`) repeats the pinned steps from a clean install on Linux, macOS, and Windows and checks three boards against their golden files. The PyGOD methods run on the CPU (`gpu=-1`) even when a GPU is present.
|
|
274
376
|
|
|
275
377
|
## Traces and Inputs
|
|
276
378
|
|
|
@@ -280,7 +382,7 @@ Methods read traces through `auditable.featurize.normalize_trace`, which accepts
|
|
|
280
382
|
|
|
281
383
|
## Reproducing CatchBench
|
|
282
384
|
|
|
283
|
-
[`benchmarks/catchbench_parity`](https://github.com/yzhao062/auditable/tree/main/benchmarks/catchbench_parity)
|
|
385
|
+
`python -m auditable.bench <task> --golden` reruns a CatchBench board with the methods of this library and exits 1 if a row differs from the board ([Leaderboard](#leaderboard)). The parity harness [`benchmarks/catchbench_parity`](https://github.com/yzhao062/auditable/tree/main/benchmarks/catchbench_parity) remains for development and is not part of the wheel. It compares every migrated method with its CatchBench or GRADE original sample by sample, at CatchBench 0.1.2 and GRADE commit `3839a57`, the last versions whose board computes those entries with its own code.
|
|
284
386
|
|
|
285
387
|
```bash
|
|
286
388
|
export GRADE_DIR=/path/to/grade # checked out at 3839a57
|
|
@@ -291,11 +393,9 @@ python benchmarks/catchbench_parity/run_parity.py board --perturb --out perturbe
|
|
|
291
393
|
python benchmarks/catchbench_parity/run_parity.py consumption board.txt perturbed.txt
|
|
292
394
|
```
|
|
293
395
|
|
|
294
|
-
The checks compare every migrated method with its CatchBench or GRADE original on the real corpora, sample by sample and bit for bit: per-run scores, per-step scores, out-of-fold predictions, configuration findings, and the features underneath them. The board run replaces CatchBench's method factories with library adapters, so the main tables and every breakdown and robustness table are computed by the library, while the original scoring functions of migrated methods are replaced by functions that raise. Its output must match the board computed before migration byte for byte. The consumption run perturbs every library output and confirms that each migrated row changes and no other row does.
|
|
295
|
-
|
|
296
396
|
## Adding a Method
|
|
297
397
|
|
|
298
|
-
Subclass the family base class in `auditable.base`, give it a `MethodCard`, and register it with `@register
|
|
398
|
+
Subclass the family base class in `auditable.base`, give it a `MethodCard`, and register it with `@register`, which makes it reachable by id (`get_method`, `--methods`) and adds it to the default rows of the detect and pre boards. To try a method first, pass it to `leaderboard(task, extra=[...])` without registering ([Put Your Method on the Board](#put-your-method-on-the-board)). The [method contract](https://github.com/yzhao062/auditable/blob/main/docs/methods-contract.md) lists the conventions: input forms, label alignment for supervised step methods, output order, optional dependencies, the tests a method needs, and how to add a parity check when the method reproduces a CatchBench row.
|
|
299
399
|
|
|
300
400
|
## Records and Graph Analysis
|
|
301
401
|
|
|
@@ -309,14 +409,26 @@ Records are content-addressed and hash-chained, and that property is limited. A
|
|
|
309
409
|
|
|
310
410
|
## Related Projects
|
|
311
411
|
|
|
312
|
-
- [CatchBench](https://github.com/yzhao062/catchbench): the benchmark
|
|
313
|
-
- [agentpile](https://github.com/yzhao062/agentpile): loads public agent-trace datasets, trace files and captured runs into one record, which `from_agentpile` reads.
|
|
412
|
+
- [CatchBench](https://github.com/yzhao062/catchbench): the paper and benchmark whose board the leaderboards reproduce (arXiv:2608.22808).
|
|
413
|
+
- [agentpile](https://github.com/yzhao062/agentpile): the converter that loads public agent-trace datasets, trace files and captured runs into one record, which `from_agentpile` reads.
|
|
314
414
|
- [GRADE](https://arxiv.org/abs/2606.22741): the typed two-layer graph of agent execution and dependency that the structural features read.
|
|
315
415
|
- [awesome-auditable-ai](https://github.com/yzhao062/awesome-auditable-ai): a reading list on AI agent reliability and auditing.
|
|
316
416
|
|
|
317
417
|
## Citation
|
|
318
418
|
|
|
319
|
-
If you use `auditable`, please cite
|
|
419
|
+
If you use `auditable`, please cite Auditable Agents:
|
|
420
|
+
|
|
421
|
+
```bibtex
|
|
422
|
+
@misc{nian2026auditable,
|
|
423
|
+
title = {Auditable Agents},
|
|
424
|
+
author = {Nian, Yi and Yuan, Aojie and Zhang, Haiyue and Li, Jiate and Li, Li and Hu, Xiyang and Wei, Hua and Xiao, Xiongye and Xiao, Chaowei and Zhao, Yue},
|
|
425
|
+
year = {2026},
|
|
426
|
+
eprint = {2604.05485},
|
|
427
|
+
archivePrefix = {arXiv}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
If you use the structural features or the benchmark, please also cite GRADE and CatchBench, and for each method, the work behind it (its `MethodCard.cite`):
|
|
320
432
|
|
|
321
433
|
```bibtex
|
|
322
434
|
@article{zhao2026grade,
|
|
@@ -9,20 +9,114 @@
|
|
|
9
9
|
|
|
10
10
|
# auditable
|
|
11
11
|
|
|
12
|
-
**A library of auditing methods for AI agents: configuration audits before a run, online monitors while it runs, and failure detection and fault localization after it
|
|
12
|
+
**A library of auditing methods for AI agents, with a leaderboard for each task: configuration audits before a run, online monitors while it runs, and failure detection and fault localization after it.**
|
|
13
13
|
|
|
14
14
|
[](https://pypi.org/project/auditable/)
|
|
15
15
|
[](https://pypi.org/project/auditable/)
|
|
16
16
|
[](https://github.com/yzhao062/auditable/blob/main/LICENSE)
|
|
17
17
|
[](https://github.com/yzhao062/auditable/actions/workflows/ci.yml)
|
|
18
|
+
[](https://colab.research.google.com/github/yzhao062/auditable/blob/main/examples/leaderboard.ipynb)
|
|
18
19
|
|
|
19
|
-
[Quick Start](#quick-start) · [Real Agent Traces](#real-agent-traces) · [Methods](#methods) · [Install](#install) · [
|
|
20
|
+
[Leaderboard](#leaderboard) · [Put Your Method on the Board](#put-your-method-on-the-board) · [Tasks](#tasks) · [Quick Start](#quick-start) · [Real Agent Traces](#real-agent-traces) · [Methods](#methods) · [Install](#install) · [Adding a Method](#adding-a-method) · [Citation](#citation)
|
|
20
21
|
|
|
21
22
|
</div>
|
|
22
23
|
|
|
24
|
+
`auditable` is a library of auditing methods for AI agents with a leaderboard for each task: one command reruns the [CatchBench](https://github.com/yzhao062/catchbench) board on your machine, and a method you write gets a rank on the same population and protocol.
|
|
25
|
+
|
|
26
|
+
## Leaderboard
|
|
27
|
+
|
|
28
|
+
Did the run fail? The board below ranks 17 methods by ROC-AUC on 376 SWE-Gym OpenHands runs, 188 of which failed. One command reruns it on the population and protocol of the CatchBench board:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install "auditable[bench]"
|
|
32
|
+
python -m auditable.bench detect/swegym
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Rank | Method | Algorithm | ROC-AUC |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| 1 | `run.pygod_gaan` | GAAN adversarial detector (PyGOD) | 0.850 |
|
|
38
|
+
| 2 | `run.lr_spline_flat` | Logistic regression on a cubic spline of run size, a control | 0.832 |
|
|
39
|
+
| 3 | `run.gsafeguard_lite` | G-Safeguard-Lite (supervised graph classifier) | 0.828 |
|
|
40
|
+
| 4 | `run.lr_full` | Logistic regression on all raw layers (GRADE full) | 0.819 |
|
|
41
|
+
| 5 | `run.lr_flatdep` | Logistic regression on size and dependency (GRADE flatdep) | 0.804 |
|
|
42
|
+
| 6 | `run.guardian_lite` | GUARDIAN-Lite (attribute-reconstruction autoencoder) | 0.767 |
|
|
43
|
+
| 7 | `run.pyod_ecod` | ECOD on run size | 0.765 |
|
|
44
|
+
| 8 | `run.pygod_conad` | CONAD contrastive graph autoencoder (PyGOD) | 0.750 |
|
|
45
|
+
| 9 | `run.lr_flat` | Logistic regression on run size (GRADE flat), the size baseline | 0.663 |
|
|
46
|
+
| 10 | `run.pyod_copod` | COPOD on run size | 0.625 |
|
|
47
|
+
| 11 | `run.pygod_anomalydae` | AnomalyDAE dual autoencoder (PyGOD) | 0.592 |
|
|
48
|
+
| 12 | `run.pyod_lof` | Local outlier factor on run size | 0.584 |
|
|
49
|
+
| 13 | `run.pyod_iforest` | Isolation Forest on run size | 0.571 |
|
|
50
|
+
| 14 | `run.pygod_dominant` | DOMINANT graph autoencoder (PyGOD) | 0.547 |
|
|
51
|
+
| 15 | `run.random` | Random run scores, a control | 0.483 |
|
|
52
|
+
| 16 | `run.pyod_knn` | KNN distance on run size | 0.446 |
|
|
53
|
+
| 17 | `run.pyod_hbos` | HBOS on run size | 0.319 |
|
|
54
|
+
|
|
55
|
+
PyGOD's GAAN, a literature baseline, leads this board at 0.850. Second, at 0.832, is the flexible size control `run.lr_spline_flat`: a cubic spline of the same four run-size counts the linear size baseline `run.lr_flat` reads (0.663). Run size alone, fitted without a linear restriction, ranks above G-Safeguard-Lite and above both GRADE dependency regressions (0.819 and 0.804), so this board does not show that dependency structure predicts failure beyond size, the reading GRADE retired on 2026-09-08. Read a new row against both size controls; on tau-bench the order changes, and `run.lr_flatdep` (0.665) ranks above the spline control (0.631).
|
|
56
|
+
|
|
57
|
+
The 16 CatchBench rows equal the CatchBench board, which was computed on Linux; the spline control is not a board row, and its reference value is the `size (spline)` arm of CatchBench's committed size audit. On macOS, `run.pyod_lof` prints 0.582 instead of 0.584, and `--golden` reports that as a platform value and not as a mismatch. The first run downloads the data through agentpile; later runs load SWE-Gym from the cache in about 3 seconds, and the full board takes about 70 seconds on a laptop CPU, of which PyGOD GAAN is the slowest row at about 37 seconds. The graph rows need `auditable[graph-ad]` and the PyG sampling backend ([Install](#install)); without them those rows are listed as skipped with the extra named, and the rest of the board is still scored.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python -m auditable.bench # list the tasks
|
|
61
|
+
python -m auditable.bench detect/swegym --methods run.lr_flat run.pyod_ecod # score chosen rows only
|
|
62
|
+
python -m auditable.bench detect/swegym --golden # exit 1 if a row differs from the CatchBench board
|
|
63
|
+
python -m auditable.bench detect/swegym --markdown # a Markdown table; --json prints JSON
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Put Your Method on the Board
|
|
67
|
+
|
|
68
|
+
A method is a class with a `MethodCard`. Pass it as `extra=` and `leaderboard` scores it beside the default rows, under the protocol of its kind: a supervised detector gets the mean ROC-AUC over five seeds of stratified 5-fold cross-validation with a fresh estimator per fold, and an unsupervised one gets a single `fit_score` over the whole population. The snippet below adds a random forest on run size, a deliberately plain method:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
72
|
+
|
|
73
|
+
from auditable.base import BaseRunDetector, MethodCard
|
|
74
|
+
from auditable.bench import leaderboard
|
|
75
|
+
from auditable.featurize import grade_layer_matrix
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ForestOnSize(BaseRunDetector):
|
|
79
|
+
card = MethodCard(method_id="run.forest_flat", name="Random forest on run size", family="run",
|
|
80
|
+
fit_scope="inductive", supervised=True, output_kind="score", fidelity="original")
|
|
81
|
+
|
|
82
|
+
def fit(self, runs, y=None):
|
|
83
|
+
self.model_ = RandomForestClassifier(200, random_state=0)
|
|
84
|
+
self.model_.fit(grade_layer_matrix(list(runs), "flat"), y)
|
|
85
|
+
self.decision_scores_ = self.decision_function(runs)
|
|
86
|
+
return self
|
|
87
|
+
|
|
88
|
+
def decision_function(self, runs):
|
|
89
|
+
return self.model_.predict_proba(grade_layer_matrix(list(runs), "flat"))[:, 1]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
board = leaderboard("detect/swegym", extra=[ForestOnSize])
|
|
93
|
+
print(board.rank_of("run.forest_flat"), board["run.forest_flat"].scores)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The last line prints the rank of the new row among all rows and its scores. `print(board)` shows the full table, with the CatchBench value in brackets beside each row that has one, and `board.to_markdown()`, `board.to_dict()` and `board.compare_golden()` export it. `methods=[...]` scores only the rows you name, which is quick while you iterate (`leaderboard("detect/swegym", methods=["run.lr_flat", ForestOnSize])`), and `Entry(method, kwargs={...}, label="...", key="...")` adds a row for a variant with constructor arguments (give each variant its own key, because a row that keeps the method id is compared with that method's CatchBench value). A method that raises is listed as an error and the rest of the board is still scored. The [method contract](https://github.com/yzhao062/auditable/blob/main/docs/methods-contract.md) lists the conventions for the four families.
|
|
97
|
+
|
|
98
|
+
## Tasks
|
|
99
|
+
|
|
100
|
+
Six tasks ship with 0.4.0. Each fixes a population and a protocol, so every method on a board is scored on the same data in the same way. `detect` scores the run detectors, `localize` the step locators, `live` the online monitors (run detectors also take part, scored on prefixes of the run), and `pre` the configuration auditors.
|
|
101
|
+
|
|
102
|
+
| Task | Question | Population | Metric |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| `detect/swegym` | Did the run fail? | SWE-Gym OpenHands trajectories, one scaffold: 376 runs, 188 failed | ROC-AUC |
|
|
105
|
+
| `detect/tau` | Did the run fail? | tau-bench: 660 runs, 363 failed | ROC-AUC |
|
|
106
|
+
| `localize/whoandwhen` | Which step was the decisive mistake? | Who&When: 126 failed multi-agent runs with a human mistake step | top-1, top-3, MRR (mean reciprocal rank) |
|
|
107
|
+
| `live/swegym` | Can the failure be caught early? | SWE-Gym runs, read as prefixes of 25, 50, 75 and 100 percent of their steps | mean prefix ROC-AUC |
|
|
108
|
+
| `live/tau` | Can the failure be caught early? | tau-bench runs, read as prefixes of 25, 50, 75 and 100 percent of their steps | mean prefix ROC-AUC |
|
|
109
|
+
| `pre/over_privilege` | Is the agent configuration over-privileged before it runs? | 1,187 configurations from six sources | F1, precision, recall |
|
|
110
|
+
|
|
111
|
+
Every task id works with the commands above: `python -m auditable.bench <task>` reruns the board, and `leaderboard("<task>", extra=[...])` places a method on it. The LLM judges on `localize/whoandwhen` and `pre/over_privilege` are replayed from committed caches, so those boards need no API call.
|
|
112
|
+
|
|
113
|
+
The CatchBench board shows how the other tasks read. On Who&When localization, the best LLM judges (all-at-once) reach top-1 of about 0.42 to 0.45 against 0.119 for random, and the structural blast rule ties the position prior at 0.159 top-1. The tau-bench detection board is led by `run.lr_flatdep` and `run.lr_full`, tied at 0.665, against 0.619 for the size baseline. For over-privilege, the best rule scanner `config.owasp_asi_combined` reaches F1 0.654, flagging every configuration reaches 0.601, and an LLM judge reaches 0.695.
|
|
114
|
+
|
|
115
|
+
## What Is in the Library
|
|
116
|
+
|
|
23
117
|
`auditable` collects methods that audit LLM agents at three points: an agent configuration before it runs (PRE), a run while it streams (LIVE), and a finished run (POST). Methods are grouped into four families, and each family has one interface, in the way PyOD gives outlier detectors one interface. The library holds baselines from the literature (PyOD and PyGOD detectors, GUARDIAN, G-Safeguard, the Who&When LLM-judge protocols, OWASP-style configuration rules) beside the methods introduced with GRADE and CatchBench.
|
|
24
118
|
|
|
25
|
-
[CatchBench](https://github.com/yzhao062/catchbench) is the benchmark
|
|
119
|
+
[CatchBench](https://github.com/yzhao062/catchbench) is the paper and benchmark whose board the leaderboards reproduce: `auditable.bench` carries its populations and evaluation protocols, and every method here that appears on its board reproduces its row sample by sample ([Reproducing CatchBench](#reproducing-catchbench)). [agentpile](https://github.com/yzhao062/agentpile) is the converter that reads public agent-trace datasets into the one record the methods consume ([Real Agent Traces](#real-agent-traces)).
|
|
26
120
|
|
|
27
121
|
## Quick Start
|
|
28
122
|
|
|
@@ -147,6 +241,7 @@ Configuration rules and the step-by-step and binary-search LLM judges run on the
|
|
|
147
241
|
| Supervised | `run.lr_flat` | Logistic regression on run size (GRADE flat) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
148
242
|
| Supervised | `run.lr_flatdep` | Logistic regression on size and dependency (GRADE flatdep) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
149
243
|
| Supervised | `run.lr_full` | Logistic regression on all raw layers (GRADE full) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
244
|
+
| Supervised | `run.lr_spline_flat` | Logistic regression on a cubic spline of run size (flexible size control) | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
150
245
|
| PyOD detector | `run.pyod_copod` | COPOD on run size | [COPOD (Li et al., 2020)](https://arxiv.org/abs/2009.09463) |
|
|
151
246
|
| PyOD detector | `run.pyod_ecod` | ECOD on run size | [ECOD (Li et al., 2022)](https://arxiv.org/abs/2201.00382) |
|
|
152
247
|
| PyOD detector | `run.pyod_hbos` | HBOS on run size | HBOS (Goldstein and Dengel, 2012) |
|
|
@@ -189,15 +284,16 @@ Configuration rules and the step-by-step and binary-search LLM judges run on the
|
|
|
189
284
|
|
|
190
285
|
```bash
|
|
191
286
|
pip install auditable # core: the registry and the configuration rules, no third-party dependencies
|
|
287
|
+
pip install "auditable[bench]" # the leaderboards: PyOD, scikit-learn, NumPy, NetworkX, and agentpile with its Hugging Face loaders
|
|
192
288
|
pip install "auditable[tabular-ad]" # run, step, and online methods on trace features (NumPy, scikit-learn, PyOD, NetworkX)
|
|
193
289
|
pip install "auditable[agentpile]" # read public datasets, trace files and captured runs through agentpile
|
|
194
290
|
```
|
|
195
291
|
|
|
196
|
-
The examples, tests and benchmark harnesses are in the repository, not the wheel; to run them, install from a clone with `pip install -e ".[dev,tabular-ad]"`.
|
|
292
|
+
The examples, tests and benchmark harnesses are in the repository, not the wheel; to run them, install from a clone with `pip install -e ".[dev,tabular-ad,bench]"`.
|
|
197
293
|
|
|
198
294
|
The LLM judges take any `prompt -> reply` function as their backend; `OpenAICompatibleBackend` reaches an OpenAI-compatible endpoint with the standard library alone.
|
|
199
295
|
|
|
200
|
-
The graph methods (PyGOD detectors, GUARDIAN-Lite, G-Safeguard-Lite) need torch, PyTorch Geometric, PyGOD, and a sampling backend built for the installed torch, which pip cannot resolve from the extra. Install torch and the matching `pyg-lib` wheel first:
|
|
296
|
+
The graph methods (PyGOD detectors, GUARDIAN-Lite, G-Safeguard-Lite), and so the graph rows of the leaderboards, need torch, PyTorch Geometric, PyGOD, and a sampling backend built for the installed torch, which pip cannot resolve from the extra; they need Python 3.10 or newer, since torch 2.12.1 and `pyg-lib` publish no wheels for 3.9. Install torch and the matching `pyg-lib` wheel first (the same steps on Linux, macOS on Apple silicon, and Windows):
|
|
201
297
|
|
|
202
298
|
```bash
|
|
203
299
|
pip install "torch==2.12.1" --index-url https://download.pytorch.org/whl/cpu
|
|
@@ -206,7 +302,7 @@ pip install "auditable[graph-ad]"
|
|
|
206
302
|
python -c "import torch_geometric.typing as t; assert t.WITH_PYG_LIB"
|
|
207
303
|
```
|
|
208
304
|
|
|
209
|
-
Where torch is already installed, as on Colab, keep it: skip the first line, and in the `pyg-lib` URL replace `2.12.1+cpu` with the installed version, which `python -c "import torch; print(torch.__version__)"` prints
|
|
305
|
+
Where torch is already installed, as on Colab, keep it: skip the first line, and in the `pyg-lib` URL replace `2.12.1+cpu` with the installed version, which `python -c "import torch; print(torch.__version__)"` prints; the pinned CPU build would replace Colab's torch and break its preinstalled torchvision. PyG does not publish wheels for every patch release, so when pip finds no `pyg_lib` at that URL, use the `.0` release with the same CUDA tag: Colab's torch `2.14.1+cu130` takes `https://data.pyg.org/whl/torch-2.14.0+cu130.html`. The graph-ad install was checked for 0.4.0 on a Colab T4 runtime with its torch 2.14.1+cu130, and for 0.3.0 on Colab CPU and T4 runtimes with Colab's torch and on Linux aarch64 (DGX Spark) with the pinned one; a weekly workflow (`bench-platforms.yml`) repeats the pinned steps from a clean install on Linux, macOS, and Windows and checks three boards against their golden files. The PyGOD methods run on the CPU (`gpu=-1`) even when a GPU is present.
|
|
210
306
|
|
|
211
307
|
## Traces and Inputs
|
|
212
308
|
|
|
@@ -216,7 +312,7 @@ Methods read traces through `auditable.featurize.normalize_trace`, which accepts
|
|
|
216
312
|
|
|
217
313
|
## Reproducing CatchBench
|
|
218
314
|
|
|
219
|
-
[`benchmarks/catchbench_parity`](https://github.com/yzhao062/auditable/tree/main/benchmarks/catchbench_parity)
|
|
315
|
+
`python -m auditable.bench <task> --golden` reruns a CatchBench board with the methods of this library and exits 1 if a row differs from the board ([Leaderboard](#leaderboard)). The parity harness [`benchmarks/catchbench_parity`](https://github.com/yzhao062/auditable/tree/main/benchmarks/catchbench_parity) remains for development and is not part of the wheel. It compares every migrated method with its CatchBench or GRADE original sample by sample, at CatchBench 0.1.2 and GRADE commit `3839a57`, the last versions whose board computes those entries with its own code.
|
|
220
316
|
|
|
221
317
|
```bash
|
|
222
318
|
export GRADE_DIR=/path/to/grade # checked out at 3839a57
|
|
@@ -227,11 +323,9 @@ python benchmarks/catchbench_parity/run_parity.py board --perturb --out perturbe
|
|
|
227
323
|
python benchmarks/catchbench_parity/run_parity.py consumption board.txt perturbed.txt
|
|
228
324
|
```
|
|
229
325
|
|
|
230
|
-
The checks compare every migrated method with its CatchBench or GRADE original on the real corpora, sample by sample and bit for bit: per-run scores, per-step scores, out-of-fold predictions, configuration findings, and the features underneath them. The board run replaces CatchBench's method factories with library adapters, so the main tables and every breakdown and robustness table are computed by the library, while the original scoring functions of migrated methods are replaced by functions that raise. Its output must match the board computed before migration byte for byte. The consumption run perturbs every library output and confirms that each migrated row changes and no other row does.
|
|
231
|
-
|
|
232
326
|
## Adding a Method
|
|
233
327
|
|
|
234
|
-
Subclass the family base class in `auditable.base`, give it a `MethodCard`, and register it with `@register
|
|
328
|
+
Subclass the family base class in `auditable.base`, give it a `MethodCard`, and register it with `@register`, which makes it reachable by id (`get_method`, `--methods`) and adds it to the default rows of the detect and pre boards. To try a method first, pass it to `leaderboard(task, extra=[...])` without registering ([Put Your Method on the Board](#put-your-method-on-the-board)). The [method contract](https://github.com/yzhao062/auditable/blob/main/docs/methods-contract.md) lists the conventions: input forms, label alignment for supervised step methods, output order, optional dependencies, the tests a method needs, and how to add a parity check when the method reproduces a CatchBench row.
|
|
235
329
|
|
|
236
330
|
## Records and Graph Analysis
|
|
237
331
|
|
|
@@ -245,14 +339,26 @@ Records are content-addressed and hash-chained, and that property is limited. A
|
|
|
245
339
|
|
|
246
340
|
## Related Projects
|
|
247
341
|
|
|
248
|
-
- [CatchBench](https://github.com/yzhao062/catchbench): the benchmark
|
|
249
|
-
- [agentpile](https://github.com/yzhao062/agentpile): loads public agent-trace datasets, trace files and captured runs into one record, which `from_agentpile` reads.
|
|
342
|
+
- [CatchBench](https://github.com/yzhao062/catchbench): the paper and benchmark whose board the leaderboards reproduce (arXiv:2608.22808).
|
|
343
|
+
- [agentpile](https://github.com/yzhao062/agentpile): the converter that loads public agent-trace datasets, trace files and captured runs into one record, which `from_agentpile` reads.
|
|
250
344
|
- [GRADE](https://arxiv.org/abs/2606.22741): the typed two-layer graph of agent execution and dependency that the structural features read.
|
|
251
345
|
- [awesome-auditable-ai](https://github.com/yzhao062/awesome-auditable-ai): a reading list on AI agent reliability and auditing.
|
|
252
346
|
|
|
253
347
|
## Citation
|
|
254
348
|
|
|
255
|
-
If you use `auditable`, please cite
|
|
349
|
+
If you use `auditable`, please cite Auditable Agents:
|
|
350
|
+
|
|
351
|
+
```bibtex
|
|
352
|
+
@misc{nian2026auditable,
|
|
353
|
+
title = {Auditable Agents},
|
|
354
|
+
author = {Nian, Yi and Yuan, Aojie and Zhang, Haiyue and Li, Jiate and Li, Li and Hu, Xiyang and Wei, Hua and Xiao, Xiongye and Xiao, Chaowei and Zhao, Yue},
|
|
355
|
+
year = {2026},
|
|
356
|
+
eprint = {2604.05485},
|
|
357
|
+
archivePrefix = {arXiv}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
If you use the structural features or the benchmark, please also cite GRADE and CatchBench, and for each method, the work behind it (its `MethodCard.cite`):
|
|
256
362
|
|
|
257
363
|
```bibtex
|
|
258
364
|
@article{zhao2026grade,
|
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# auditable Examples
|
|
2
2
|
|
|
3
|
+
## Leaderboards: Rerun the Board and Add Your Method
|
|
4
|
+
|
|
5
|
+
`auditable.bench` scores auditing methods on frozen populations with frozen protocols, ported from [CatchBench](https://github.com/yzhao062/catchbench), the paper and benchmark whose board these leaderboards reproduce. One call reruns a board on your machine, and a method you write gets a rank on the same population and protocol as every other row. The data come through [agentpile](https://github.com/yzhao062/agentpile), which reads public agent-trace datasets into one record.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install "auditable[bench]"
|
|
9
|
+
python examples/example_leaderboard.py
|
|
10
|
+
python examples/example_add_your_method.py
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`example_leaderboard.py` prints the `detect/swegym` board: did the run fail, on 376 SWE-Gym OpenHands runs (188 failed), scored by ROC-AUC. It then restricts the board to three tabular rows (`run.lr_flat`, `run.lr_flatdep`, `run.pyod_ecod`) with `methods=[...]` and prints the full board as a Markdown table. The first call loads the runs through agentpile, and the full 17-row board takes about 70 seconds on a laptop CPU. A row whose pip extra is missing is listed as skipped with the extra named, and a row that raises is listed as an error, while the rest of the board is still scored. The graph rows (PyGOD, GUARDIAN-Lite, G-Safeguard-Lite) need `auditable[graph-ad]` plus the PyG sampling backend described in the Install section of the main README.
|
|
14
|
+
|
|
15
|
+
`example_add_your_method.py` defines one new run detector in about 20 lines: a logistic regression on the number of steps and the longest dependency span, written as a `BaseRunDetector` subclass with a full `MethodCard`. The script passes the class to `leaderboard(..., extra=[StepsAndSpan])` next to the size baseline `run.lr_flat`, `run.lr_flatdep`, `run.pyod_ecod`, and the random control, then prints the board and the rank of the new row. Copy the class, change `features` and the estimator, and the board scores your idea under the same cross-validation as every supervised row.
|
|
16
|
+
|
|
17
|
+
The same boards run from a shell:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
python -m auditable.bench # list the tasks
|
|
21
|
+
python -m auditable.bench detect/swegym --methods run.lr_flat run.pyod_ecod
|
|
22
|
+
python -m auditable.bench detect/swegym --golden # exit 1 if a row differs from the CatchBench board
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`--golden` compares each row with the CatchBench board and prints a known platform difference without failing: on macOS `run.pyod_lof` scores 0.582 where the Linux board prints 0.584. Pass another task id to score another task, and name only methods of the task's family (the live tasks take `run` and `online` methods), because a method of another family is listed as skipped.
|
|
26
|
+
|
|
27
|
+
Read a board with its baselines in view. The top row of `detect/swegym` is PyGOD's GAAN detector, a literature baseline, and `run.lr_flat` is the size baseline that belongs beside every dependency method. A linear size baseline cannot separate dependency shape from a nonlinear function of size, and GRADE retired the reading that the dependency layers predict failure beyond run size on 2026-09-08. A rank is one measurement on one population and protocol.
|
|
28
|
+
|
|
29
|
+
`leaderboard.ipynb` runs the same two steps in a notebook, with the board, the golden check, and a template detector, and it opens in Colab.
|
|
30
|
+
|
|
31
|
+
[](https://colab.research.google.com/github/yzhao062/auditable/blob/main/examples/leaderboard.ipynb)
|
|
32
|
+
|
|
3
33
|
## The Method Library
|
|
4
34
|
|
|
5
35
|
`example_methods.py` runs one method from each family of `auditable.methods` (a
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Add your own method to a board: score a new run detector on SWE-Gym next to the paper's rows.
|
|
2
|
+
|
|
3
|
+
Run from a checkout after installing the bench extra:
|
|
4
|
+
|
|
5
|
+
pip install "auditable[bench]"
|
|
6
|
+
python examples/example_add_your_method.py
|
|
7
|
+
|
|
8
|
+
The detector is a logistic regression on two hand-made features of the typed run graph: the
|
|
9
|
+
number of steps and the longest dependency span (how far back any step reaches). The board
|
|
10
|
+
scores it as it scores every supervised method, by mean ROC-AUC over five seeds of
|
|
11
|
+
stratified 5-fold cross-validation with a fresh estimator per fold.
|
|
12
|
+
|
|
13
|
+
The script prints the small board (the size baseline ``run.lr_flat``, the dependency
|
|
14
|
+
regression ``run.lr_flatdep``, ``run.pyod_ecod``, the random control, and the new row) and
|
|
15
|
+
the rank and score of the new row beside the size baseline. Read any gain over
|
|
16
|
+
``run.lr_flat`` with care. The longest span grows with run length, and GRADE retired on
|
|
17
|
+
2026-09-08 the reading that dependency structure predicts failure beyond run size, so a
|
|
18
|
+
claim for a new method needs a comparison with a size-only model fitted as flexibly as
|
|
19
|
+
yours. A rank describes a method on one population and protocol; it does not explain why
|
|
20
|
+
the method works.
|
|
21
|
+
"""
|
|
22
|
+
import numpy as np
|
|
23
|
+
from sklearn.linear_model import LogisticRegression
|
|
24
|
+
from sklearn.pipeline import make_pipeline
|
|
25
|
+
from sklearn.preprocessing import StandardScaler
|
|
26
|
+
|
|
27
|
+
from auditable.base import BaseRunDetector, MethodCard
|
|
28
|
+
from auditable.bench import leaderboard
|
|
29
|
+
from auditable.featurize import normalize_trace, run_max_span
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class StepsAndSpan(BaseRunDetector):
|
|
33
|
+
"""Run length and the longest dependency span, weighed jointly by a linear model."""
|
|
34
|
+
|
|
35
|
+
card = MethodCard(
|
|
36
|
+
method_id="run.lr_steps_span", name="Logistic regression on step count and longest span",
|
|
37
|
+
family="run", fit_scope="inductive", supervised=True, output_kind="score", fidelity="original",
|
|
38
|
+
requires=frozenset({"deps"}), extras=("tabular-ad",),
|
|
39
|
+
summary="Balanced logistic regression on the log step count and the longest dependency span.")
|
|
40
|
+
|
|
41
|
+
def features(self, runs):
|
|
42
|
+
# log1p tames the long tail of run length; the board calls this once and indexes the matrix per fold.
|
|
43
|
+
return np.array([[np.log1p(len(normalize_trace(g))), run_max_span(g)] for g in runs])
|
|
44
|
+
|
|
45
|
+
def fit(self, X, y=None):
|
|
46
|
+
X = X if getattr(X, "ndim", 0) == 2 else self.features(X)
|
|
47
|
+
self.pipeline_ = make_pipeline(StandardScaler(), LogisticRegression(class_weight="balanced")).fit(X, y)
|
|
48
|
+
self.decision_scores_ = self.pipeline_.decision_function(X)
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
def decision_function(self, X):
|
|
52
|
+
return self.pipeline_.decision_function(X if getattr(X, "ndim", 0) == 2 else self.features(X))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
# The size baseline and the paper's dependency and unsupervised rows stay on the board so the
|
|
57
|
+
# new row is read against them; extra= adds the new row to those named in methods=.
|
|
58
|
+
board = leaderboard("detect/swegym", methods=["run.lr_flat", "run.lr_flatdep", "run.pyod_ecod", "run.random"],
|
|
59
|
+
extra=[StepsAndSpan])
|
|
60
|
+
print(board)
|
|
61
|
+
key = StepsAndSpan.card.method_id
|
|
62
|
+
print(f"\n{key} ranks {board.rank_of(key)} of {len(board.ranked())}, "
|
|
63
|
+
f"ROC-AUC {board[key].scores['roc_auc']:.3f} against {board['run.lr_flat'].scores['roc_auc']:.3f} "
|
|
64
|
+
f"for the size baseline")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|