auditable 0.2.1__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.4.0/PKG-INFO +452 -0
- auditable-0.4.0/README.md +382 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/README.md +283 -228
- auditable-0.4.0/examples/example_add_your_method.py +68 -0
- auditable-0.4.0/examples/example_agentpile.py +71 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_langchain_capture.py +96 -96
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_langgraph_capture.py +101 -101
- auditable-0.4.0/examples/example_leaderboard.py +41 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_mcp_capture.py +116 -116
- auditable-0.4.0/examples/example_methods.py +62 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_payment_audit.py +113 -113
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_post_rank_run.py +80 -80
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_state_anomaly.py +119 -119
- auditable-0.4.0/examples/leaderboard.ipynb +275 -0
- {auditable-0.2.1 → auditable-0.4.0}/pyproject.toml +103 -79
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/__init__.py +96 -79
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/analysis.py +615 -615
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/audit_report.py +1055 -1055
- auditable-0.4.0/src/auditable/base.py +272 -0
- 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.2.1 → auditable-0.4.0}/src/auditable/chain.py +1 -1
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/data/__init__.py +260 -256
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/data/inject.py +225 -225
- auditable-0.4.0/src/auditable/datasets.py +96 -0
- auditable-0.4.0/src/auditable/featurize/__init__.py +72 -0
- auditable-0.4.0/src/auditable/featurize/_grade_projection.py +307 -0
- auditable-0.4.0/src/auditable/featurize/agentpile.py +497 -0
- auditable-0.4.0/src/auditable/featurize/pyg.py +43 -0
- auditable-0.4.0/src/auditable/featurize/spec.py +372 -0
- auditable-0.4.0/src/auditable/featurize/step_deps.py +97 -0
- auditable-0.4.0/src/auditable/featurize/trace.py +163 -0
- auditable-0.4.0/src/auditable/featurize/who_when.py +71 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/__init__.py +3 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/adapters/__init__.py +44 -44
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/adapters/own_record.py +142 -142
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/adapters/protocol.py +64 -64
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/adapters/tau_bench.py +138 -138
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/features.py +56 -14
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/grounding.py +332 -332
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/loader.py +250 -250
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/risk.py +130 -128
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/session.py +292 -292
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/harness/__init__.py +7 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/integrations/__init__.py +10 -10
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/integrations/langchain.py +805 -805
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/integrations/mcp.py +328 -328
- auditable-0.4.0/src/auditable/llm/__init__.py +7 -0
- auditable-0.4.0/src/auditable/llm/backend.py +87 -0
- auditable-0.4.0/src/auditable/llm/cache.py +63 -0
- auditable-0.4.0/src/auditable/llm/who_when.py +217 -0
- auditable-0.4.0/src/auditable/methods/__init__.py +134 -0
- auditable-0.4.0/src/auditable/methods/_pygod.py +101 -0
- auditable-0.4.0/src/auditable/methods/config/__init__.py +1 -0
- auditable-0.4.0/src/auditable/methods/config/llm.py +227 -0
- auditable-0.4.0/src/auditable/methods/config/rules.py +244 -0
- auditable-0.4.0/src/auditable/methods/online/__init__.py +1 -0
- auditable-0.4.0/src/auditable/methods/online/rules.py +112 -0
- auditable-0.4.0/src/auditable/methods/run/__init__.py +1 -0
- auditable-0.4.0/src/auditable/methods/run/baselines.py +64 -0
- auditable-0.4.0/src/auditable/methods/run/graph.py +410 -0
- auditable-0.4.0/src/auditable/methods/run/pyod.py +166 -0
- auditable-0.4.0/src/auditable/methods/run/supervised.py +172 -0
- auditable-0.4.0/src/auditable/methods/step/__init__.py +1 -0
- auditable-0.4.0/src/auditable/methods/step/graph.py +99 -0
- auditable-0.4.0/src/auditable/methods/step/llm.py +170 -0
- auditable-0.4.0/src/auditable/methods/step/rules.py +219 -0
- auditable-0.4.0/src/auditable/methods/step/supervised.py +139 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/record.py +5 -3
- auditable-0.4.0/tests/fixtures/agentpile_grade/README.md +3 -0
- auditable-0.4.0/tests/fixtures/agentpile_grade/agent_reward_bench_records.json +204 -0
- auditable-0.4.0/tests/fixtures/agentpile_grade/b_openhands_multi_call_turn.json +36 -0
- auditable-0.4.0/tests/fixtures/agentpile_grade/swe_agent_synthetic.json +376 -0
- auditable-0.4.0/tests/fixtures/grade_3839a57_features.json +1 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_adapters.py +352 -352
- auditable-0.4.0/tests/test_agentpile_adapter.py +540 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_analysis.py +188 -188
- 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.2.1 → auditable-0.4.0}/tests/test_data_inject.py +194 -194
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_data_module.py +144 -144
- auditable-0.4.0/tests/test_datasets.py +48 -0
- auditable-0.4.0/tests/test_featurize.py +186 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_graph_features.py +3 -1
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_graph_loader.py +293 -293
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_graph_risk.py +134 -134
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_grounding.py +249 -249
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_langchain_integration.py +292 -292
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_live_session.py +372 -372
- auditable-0.4.0/tests/test_llm.py +207 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_mcp_integration.py +264 -264
- auditable-0.4.0/tests/test_methods_config.py +527 -0
- auditable-0.4.0/tests/test_methods_graph.py +289 -0
- auditable-0.4.0/tests/test_methods_pygod_step.py +49 -0
- auditable-0.4.0/tests/test_methods_registry.py +234 -0
- auditable-0.4.0/tests/test_methods_run_tabular.py +285 -0
- auditable-0.4.0/tests/test_methods_slice.py +127 -0
- auditable-0.4.0/tests/test_methods_spline_size.py +46 -0
- auditable-0.4.0/tests/test_methods_step_rules.py +327 -0
- auditable-0.4.0/tests/test_parity_consumption.py +66 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_public_api.py +143 -99
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_session_graph.py +157 -157
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_session_graph_projection.py +141 -141
- auditable-0.4.0/tests/test_version.py +11 -0
- auditable-0.2.1/PKG-INFO +0 -392
- auditable-0.2.1/README.md +0 -345
- {auditable-0.2.1 → auditable-0.4.0}/.gitignore +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/LICENSE +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_audit_report.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_end_to_end.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_langgraph_live_replay.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_langgraph_llm_agent.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_live_monitor.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_live_replay.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_pre_lint_plan.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_standalone_report.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/examples/example_touch_capture.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/_report_figures.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/compound.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/adapters/declared_plan.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/pre.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/graph/touch.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/integrations/langgraph.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/model/__init__.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/src/auditable/report.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_audit_report.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_auditors.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_basic.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_compound.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_gate.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_graph.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_graph_execution_reach.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_langgraph_integration.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_langgraph_replay_bridge.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_pre.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_pre_coverage.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_replay.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_report.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_sink.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_touch.py +0 -0
- {auditable-0.2.1 → auditable-0.4.0}/tests/test_touch_replay_bridge.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).
|
auditable-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: auditable
|
|
3
|
+
Version: 0.4.0
|
|
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
|
+
Project-URL: Homepage, https://github.com/yzhao062/auditable
|
|
6
|
+
Project-URL: Repository, https://github.com/yzhao062/auditable
|
|
7
|
+
Project-URL: Issues, https://github.com/yzhao062/auditable/issues
|
|
8
|
+
Project-URL: Documentation, https://auditable-ai.readthedocs.io/
|
|
9
|
+
Author-email: Yue Zhao <yzhao010@usc.edu>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,agent-reliability,ai-agents,ai-safety,anomaly-detection,audit,auditing,benchmark,failure-attribution,failure-detection,fault-localization,graph-anomaly-detection,langgraph,llm,mcp,trustworthy-ai
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Provides-Extra: agentpile
|
|
26
|
+
Requires-Dist: agentpile[hf]<0.4,>=0.3; extra == 'agentpile'
|
|
27
|
+
Requires-Dist: numpy>=1.21; extra == 'agentpile'
|
|
28
|
+
Provides-Extra: anomaly
|
|
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'
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pyod>=1.1.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
41
|
+
Provides-Extra: graph
|
|
42
|
+
Requires-Dist: networkx>=3.0; extra == 'graph'
|
|
43
|
+
Provides-Extra: graph-ad
|
|
44
|
+
Requires-Dist: networkx>=3.0; extra == 'graph-ad'
|
|
45
|
+
Requires-Dist: numpy>=1.21; extra == 'graph-ad'
|
|
46
|
+
Requires-Dist: pygod>=1.1.0; extra == 'graph-ad'
|
|
47
|
+
Requires-Dist: pyod>=1.1.0; extra == 'graph-ad'
|
|
48
|
+
Requires-Dist: scikit-learn>=1.0; extra == 'graph-ad'
|
|
49
|
+
Requires-Dist: torch-geometric>=2.3; extra == 'graph-ad'
|
|
50
|
+
Requires-Dist: torch>=2.0; extra == 'graph-ad'
|
|
51
|
+
Provides-Extra: langchain
|
|
52
|
+
Requires-Dist: langchain-core<2,>=0.3.0; extra == 'langchain'
|
|
53
|
+
Provides-Extra: langgraph
|
|
54
|
+
Requires-Dist: langgraph>=1.0; (python_version >= '3.10') and extra == 'langgraph'
|
|
55
|
+
Requires-Dist: networkx>=3.0; extra == 'langgraph'
|
|
56
|
+
Provides-Extra: llm
|
|
57
|
+
Requires-Dist: openai>=1.0; extra == 'llm'
|
|
58
|
+
Provides-Extra: mcp
|
|
59
|
+
Requires-Dist: mcp<2,>=1.28.1; (python_version >= '3.10') and extra == 'mcp'
|
|
60
|
+
Provides-Extra: report
|
|
61
|
+
Requires-Dist: fpdf2>=2.7; extra == 'report'
|
|
62
|
+
Requires-Dist: matplotlib>=3.5; extra == 'report'
|
|
63
|
+
Requires-Dist: networkx>=3.0; extra == 'report'
|
|
64
|
+
Provides-Extra: tabular-ad
|
|
65
|
+
Requires-Dist: networkx>=3.0; extra == 'tabular-ad'
|
|
66
|
+
Requires-Dist: numpy>=1.21; extra == 'tabular-ad'
|
|
67
|
+
Requires-Dist: pyod>=1.1.0; extra == 'tabular-ad'
|
|
68
|
+
Requires-Dist: scikit-learn>=1.0; extra == 'tabular-ad'
|
|
69
|
+
Description-Content-Type: text/markdown
|
|
70
|
+
|
|
71
|
+
<div align="center">
|
|
72
|
+
|
|
73
|
+
<!-- logo-family -->
|
|
74
|
+
<picture>
|
|
75
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/yzhao062/auditable/main/docs/assets/brand/logo-dark.svg">
|
|
76
|
+
<img src="https://raw.githubusercontent.com/yzhao062/auditable/main/docs/assets/brand/logo.svg" alt="auditable" width="350">
|
|
77
|
+
</picture>
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# auditable
|
|
81
|
+
|
|
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.**
|
|
83
|
+
|
|
84
|
+
[](https://pypi.org/project/auditable/)
|
|
85
|
+
[](https://pypi.org/project/auditable/)
|
|
86
|
+
[](https://github.com/yzhao062/auditable/blob/main/LICENSE)
|
|
87
|
+
[](https://github.com/yzhao062/auditable/actions/workflows/ci.yml)
|
|
88
|
+
[](https://colab.research.google.com/github/yzhao062/auditable/blob/main/examples/leaderboard.ipynb)
|
|
89
|
+
|
|
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)
|
|
91
|
+
|
|
92
|
+
</div>
|
|
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
|
+
|
|
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.
|
|
188
|
+
|
|
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)).
|
|
190
|
+
|
|
191
|
+
## Quick Start
|
|
192
|
+
|
|
193
|
+
Install with `pip install "auditable[tabular-ad]"` (see [Install](#install)), then:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from sklearn.metrics import roc_auc_score
|
|
197
|
+
|
|
198
|
+
from auditable.datasets import make_traces
|
|
199
|
+
from auditable.methods import get_method
|
|
200
|
+
|
|
201
|
+
# 100 toy agent runs; each failed run holds one stale read (a step relying on old state)
|
|
202
|
+
traces, y, mistakes = make_traces(n_runs=100, contamination=0.5, random_state=0)
|
|
203
|
+
train, test = slice(0, 50), slice(50, 100)
|
|
204
|
+
|
|
205
|
+
# Did the run fail? Fit on labelled runs, then score new ones, as with a PyOD detector.
|
|
206
|
+
detector = get_method("run.lr_flatdep")()
|
|
207
|
+
detector.fit(traces[train], y[train])
|
|
208
|
+
scores = detector.decision_function(traces[test]) # one score per run, higher = more likely failed
|
|
209
|
+
print(f"run-level ROC-AUC: {roc_auc_score(y[test], scores):.3f}")
|
|
210
|
+
|
|
211
|
+
# Which step was the mistake? Rank the steps of each failed run, most suspected first.
|
|
212
|
+
locator = get_method("step.dep_anomaly")()
|
|
213
|
+
failed = [i for i in range(100) if y[i] == 1]
|
|
214
|
+
hits = sum(locator.rank(traces[i])[0] == mistakes[i] for i in failed)
|
|
215
|
+
print(f"mistake ranked first in {hits} of {len(failed)} failed runs")
|
|
216
|
+
|
|
217
|
+
# Can it be caught live? One causal alarm per arriving step (step t sees steps 0..t only).
|
|
218
|
+
monitor = get_method("online.span_z")()
|
|
219
|
+
alarms = monitor.step_alarm_scores(traces[failed[0]])
|
|
220
|
+
print(f"first alarm peak at step {alarms.argmax()}, mistake at step {mistakes[failed[0]]}")
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Every method is created from its ID with `get_method(id)()`, and the methods of one family share one interface, so trying another detector or locator changes one string. [`examples/example_methods.py`](https://github.com/yzhao062/auditable/blob/main/examples/example_methods.py) adds a configuration audit before the run.
|
|
224
|
+
|
|
225
|
+
## Real Agent Traces
|
|
226
|
+
|
|
227
|
+
[agentpile](https://github.com/yzhao062/agentpile) loads public agent-trace datasets (Who&When, TRAIL, MAST, tau-bench, SWE-Gym and others), trace files (OpenAI-style messages, OTLP JSON, Claude Code sessions, MCP tool lists) and captured runs into one record. `from_agentpile` turns those records into the traces every method reads, and `configs_from_agentpile` turns them into the configurations the config methods read. `pip install "auditable[agentpile]"` installs the conversion; the example below also runs tabular methods, so it needs `pip install "auditable[agentpile,tabular-ad]"`.
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
import agentpile as ap
|
|
231
|
+
import auditable as au
|
|
232
|
+
from auditable.datasets import load_traces
|
|
233
|
+
from auditable.methods import get_method
|
|
234
|
+
|
|
235
|
+
# Any run: name the dependency rules, since agentpile infers none when it loads.
|
|
236
|
+
runs = list(ap.load("tau_bench", limit=100))
|
|
237
|
+
traces = au.from_agentpile(runs, deps=["tool_call_id_v1", "value_flow_v1"])
|
|
238
|
+
scores = get_method("run.pyod_ecod")().fit_score(traces) # one score per run
|
|
239
|
+
y = au.run_targets(traces) # 1 failed, 0 succeeded, -1 unknown
|
|
240
|
+
|
|
241
|
+
# The same shapes as make_traces, on a public dataset; mistakes holds the labeled step (-1 if none).
|
|
242
|
+
traces, y, mistakes = load_traces("who_and_when", deps="full_context", subset="Algorithm-Generated")
|
|
243
|
+
labeled = [t for t, m in zip(traces, mistakes) if m >= 0]
|
|
244
|
+
print(get_method("step.dep_anomaly")().rank(labeled[0])[:3])
|
|
245
|
+
|
|
246
|
+
# Before a run: an MCP tools/list result plus the task the agent will get.
|
|
247
|
+
configs = au.configs_from_agentpile(next(ap.load("tools.json")), task="Summarize the open issues.")
|
|
248
|
+
print(get_method("config.owasp_asi_combined")().audit(configs[0]))
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`deps` has no default. It is a list of agentpile rules, `"recorded"` for the dependencies the record already holds, or `"full_context"` for every earlier step, and each trace's `meta` keeps the rules that ran. The default `convention="generic"` makes one step per message and one per tool call with its result. `convention="grade"` with `deps="grade"` instead reproduces the steps, dependencies and graphs of the GRADE converters that CatchBench and the GRADE paper used, for the seven corpora below. [`benchmarks/agentpile_crosscheck`](https://github.com/yzhao062/auditable/tree/main/benchmarks/agentpile_crosscheck) compares that output with GRADE's run by run.
|
|
252
|
+
|
|
253
|
+
| Corpus | `load_traces` arguments | Runs in GRADE's output | Result |
|
|
254
|
+
|---|---|---|---|
|
|
255
|
+
| Who&When | `"who_and_when", convention="grade", deps="grade"` | 184 | equal; the 126 localization labels match |
|
|
256
|
+
| tau-bench | `"tau_bench", convention="grade", deps="grade", model=[...]` | 660 | equal |
|
|
257
|
+
| tau2-bench | `"tau2_bench", convention="grade", deps="grade", model=...` | 278 | equal to the frozen rule `tau2-frozen-v1`, since GRADE's own tau2 output changes with the hash seed |
|
|
258
|
+
| SWE-Gym | `"swe_gym_openhands", convention="grade", deps="grade"` | 376 | equal |
|
|
259
|
+
| AgentRewardBench | `"agent_reward_bench", convention="grade", deps="grade"` | 600 | equal on 3 runs; on the other 597, GRADE adds two steps for the page after the last action, which agentpile does not create, and the steps before them are equal |
|
|
260
|
+
| SWE-rebench OpenHands | `"swe_rebench_openhands", convention="grade", deps="grade"` | 600 | equal |
|
|
261
|
+
| SWE-agent | `"swe_agent_nebius", convention="grade", deps="grade"` | 600 | equal |
|
|
262
|
+
|
|
263
|
+
These results hold on the dataset revisions agentpile 0.3.0 loads by default. On any other revision, `convention="grade"` raises an error rather than return steps nobody has compared with GRADE's, and `convention="generic"` reads the run without that guarantee.
|
|
264
|
+
|
|
265
|
+
In the generic convention, a `user` step is a step the record attributes to the user role, which is not always text a person typed (a session's compact summary or a task notification has that role too). Content given as a list of parts (dicts with a string `type`, possibly mixed with bare strings) keeps its text; an image, a document or any other non-text part becomes a placeholder such as `[image]` and its other fields are dropped, while any other list stays JSON. `meta["report"]` counts the replaced parts and accounts for every step of the record.
|
|
266
|
+
|
|
267
|
+
A run with subagent trajectories (a Claude Code session with subagents, an Agents SDK handoff) raises an error, since this version does not flatten them into one trace. `from_agentpile(run.subagents, ...)` reads each subagent as a trace of its own, and `subagents="skip"` reads the top-level trajectory only; on the Claude Code sessions with subagents we measured, skipping left out about half of the steps.
|
|
268
|
+
|
|
269
|
+
## Methods
|
|
270
|
+
|
|
271
|
+
Configuration rules and the step-by-step and binary-search LLM judges run on the core install, graph detectors need the `graph-ad` extra, and every other method needs `tabular-ad`. The tables are generated from the registry: `list_methods(family=...)` returns each method's card, and `method_table(detailed=True)` prints the fit scope, fidelity to the source, extras, and the CatchBench row each method reproduces. The [method contract](https://github.com/yzhao062/auditable/blob/main/docs/methods-contract.md) defines the four interfaces on one page.
|
|
272
|
+
|
|
273
|
+
### Config: Is the Agent Over-Privileged Before It Runs?
|
|
274
|
+
|
|
275
|
+
`auditor.audit(config)` reads an `AgentConfig` (the declared capabilities and the task text) and returns a finding: `flag` (True, False, or None to abstain), the `excess` capabilities, and the `rules` that fired.
|
|
276
|
+
|
|
277
|
+
<!-- methods:config:start -->
|
|
278
|
+
| Type | Method | Algorithm | Reference |
|
|
279
|
+
|---|---|---|---|
|
|
280
|
+
| Rule | `config.flag_risky_perms` | Flag risky permission levels | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
281
|
+
| Rule | `config.owasp_asi_combined` | Combined OWASP and CWE scanner | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
282
|
+
| Rule | `config.owasp_excess_functionality` | Excess functionality | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
283
|
+
| Rule | `config.owasp_excess_permissions` | Excess permission level | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
284
|
+
| Rule | `config.owasp_privilege_escalation` | Unrequested privilege management | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
285
|
+
| Rule | `config.sensitive_access` | Unrequested sensitive access | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
286
|
+
| Rule | `config.unrequested_high_impact` | Unrequested high-impact action | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
287
|
+
| LLM judge | `config.llm_needed` | LLM judge, needed capabilities | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
288
|
+
<!-- methods:config:end -->
|
|
289
|
+
|
|
290
|
+
### Online: Can the Failure Be Caught While the Run Streams?
|
|
291
|
+
|
|
292
|
+
`monitor.step_alarm_scores(trace)` returns one alarm per step, and the alarm at step t reads steps 0 to t only.
|
|
293
|
+
|
|
294
|
+
<!-- methods:online:start -->
|
|
295
|
+
| Type | Method | Algorithm | Reference |
|
|
296
|
+
|---|---|---|---|
|
|
297
|
+
| Rule | `online.dep_count_z` | Causal z-score of dependency count | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
298
|
+
| Rule | `online.dep_span` | Mean dependency span (running) | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
299
|
+
| Rule | `online.raw_span` | Raw dependency span | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
300
|
+
| Rule | `online.span_z` | Causal z-score of dependency span | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
301
|
+
| Baseline | `online.random` | Random alarm scores | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
302
|
+
<!-- methods:online:end -->
|
|
303
|
+
|
|
304
|
+
### Run: Did the Run Fail?
|
|
305
|
+
|
|
306
|
+
`detector.fit(traces, y)`, then `detector.decision_function(traces)`: one score per run, higher meaning more likely failed. Unsupervised detectors ignore `y`. Transductive detectors (the PyGOD detectors and GUARDIAN-Lite) fit and score one batch together with `detector.fit_score(traces)`.
|
|
307
|
+
|
|
308
|
+
<!-- methods:run:start -->
|
|
309
|
+
| Type | Method | Algorithm | Reference |
|
|
310
|
+
|---|---|---|---|
|
|
311
|
+
| Supervised | `run.lr_flat` | Logistic regression on run size (GRADE flat) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
312
|
+
| Supervised | `run.lr_flatdep` | Logistic regression on size and dependency (GRADE flatdep) | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
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) |
|
|
315
|
+
| PyOD detector | `run.pyod_copod` | COPOD on run size | [COPOD (Li et al., 2020)](https://arxiv.org/abs/2009.09463) |
|
|
316
|
+
| PyOD detector | `run.pyod_ecod` | ECOD on run size | [ECOD (Li et al., 2022)](https://arxiv.org/abs/2201.00382) |
|
|
317
|
+
| PyOD detector | `run.pyod_hbos` | HBOS on run size | HBOS (Goldstein and Dengel, 2012) |
|
|
318
|
+
| PyOD detector | `run.pyod_iforest` | Isolation Forest on run size | [Isolation Forest (Liu et al., 2008)](https://doi.org/10.1109/ICDM.2008.17) |
|
|
319
|
+
| PyOD detector | `run.pyod_knn` | KNN distance on run size | KNN outlier score (Ramaswamy et al., 2000) |
|
|
320
|
+
| PyOD detector | `run.pyod_lof` | Local outlier factor on run size | [LOF (Breunig et al., 2000)](https://doi.org/10.1145/335191.335388) |
|
|
321
|
+
| Graph detector | `run.gsafeguard_lite` | G-Safeguard-Lite (supervised graph classifier) | [G-Safeguard (Wang et al., 2025)](https://arxiv.org/abs/2502.11127) |
|
|
322
|
+
| Graph detector | `run.guardian_lite` | GUARDIAN-Lite (attribute-reconstruction autoencoder) | [GUARDIAN (Zhou et al., 2025)](https://arxiv.org/abs/2505.19234) |
|
|
323
|
+
| Graph detector | `run.pygod_anomalydae` | AnomalyDAE dual autoencoder (PyGOD) | AnomalyDAE (Fan et al., 2020) |
|
|
324
|
+
| Graph detector | `run.pygod_conad` | CONAD contrastive graph autoencoder (PyGOD) | CONAD (Xu et al., 2022) |
|
|
325
|
+
| Graph detector | `run.pygod_dominant` | DOMINANT graph autoencoder (PyGOD) | [DOMINANT (Ding et al., 2019)](https://doi.org/10.1137/1.9781611975673.67) |
|
|
326
|
+
| Graph detector | `run.pygod_gaan` | GAAN adversarial detector (PyGOD) | GAAN (Chen et al., 2020) |
|
|
327
|
+
| Baseline | `run.random` | Random run scores | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
328
|
+
<!-- methods:run:end -->
|
|
329
|
+
|
|
330
|
+
### Step: Which Step Was the Decisive Mistake?
|
|
331
|
+
|
|
332
|
+
`locator.rank(trace)` orders the steps of a failed run, most suspected first, and `locator.step_scores(trace)` gives their scores. Supervised locators are first fitted with `fit(traces, y)`, where `y` holds the `idx` of each run's mistake. The step-by-step and binary-search LLM judges name one step with `predict_step(trace)`.
|
|
333
|
+
|
|
334
|
+
<!-- methods:step:start -->
|
|
335
|
+
| Type | Method | Algorithm | Reference |
|
|
336
|
+
|---|---|---|---|
|
|
337
|
+
| Rule | `step.blast` | Blast radius (downstream reach) | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
338
|
+
| Rule | `step.degree` | Dependency degree | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
339
|
+
| Rule | `step.dep_anomaly` | Dependency anomaly (within-run z-score) | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
340
|
+
| Rule | `step.has_dep` | Has a dependency | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
341
|
+
| Rule | `step.max_span` | Maximum dependency span | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
342
|
+
| Rule | `step.position_early_rule` | Early-position prior | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
343
|
+
| Supervised | `step.exec_rank` | Execution-feature step ranker | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
344
|
+
| Supervised | `step.position_supervised` | Early-fault position prior | [GRADE (Zhao, 2026)](https://arxiv.org/abs/2606.22741) |
|
|
345
|
+
| Graph detector | `step.pygod_dominant_sg4` | DOMINANT on the step dependency graph | [DOMINANT (Ding et al., 2019)](https://doi.org/10.1137/1.9781611975673.67) |
|
|
346
|
+
| Graph detector | `step.pygod_dominant_ww8` | DOMINANT on the Who&When step matrix | [DOMINANT (Ding et al., 2019)](https://doi.org/10.1137/1.9781611975673.67) |
|
|
347
|
+
| LLM judge | `step.llm_all_at_once` | LLM judge, all-at-once | [Who&When (Zhang et al., 2025)](https://arxiv.org/abs/2505.00212) |
|
|
348
|
+
| LLM judge | `step.llm_binary_search` | LLM judge, binary-search | [Who&When (Zhang et al., 2025)](https://arxiv.org/abs/2505.00212) |
|
|
349
|
+
| LLM judge | `step.llm_step_by_step` | LLM judge, step-by-step | [Who&When (Zhang et al., 2025)](https://arxiv.org/abs/2505.00212) |
|
|
350
|
+
| Baseline | `step.random` | Random step scores | [CatchBench (2026)](https://arxiv.org/abs/2608.22808) |
|
|
351
|
+
<!-- methods:step:end -->
|
|
352
|
+
|
|
353
|
+
## Install
|
|
354
|
+
|
|
355
|
+
```bash
|
|
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
|
|
358
|
+
pip install "auditable[tabular-ad]" # run, step, and online methods on trace features (NumPy, scikit-learn, PyOD, NetworkX)
|
|
359
|
+
pip install "auditable[agentpile]" # read public datasets, trace files and captured runs through agentpile
|
|
360
|
+
```
|
|
361
|
+
|
|
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]"`.
|
|
363
|
+
|
|
364
|
+
The LLM judges take any `prompt -> reply` function as their backend; `OpenAICompatibleBackend` reaches an OpenAI-compatible endpoint with the standard library alone.
|
|
365
|
+
|
|
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):
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
pip install "torch==2.12.1" --index-url https://download.pytorch.org/whl/cpu
|
|
370
|
+
pip install pyg_lib -f https://data.pyg.org/whl/torch-2.12.1+cpu.html
|
|
371
|
+
pip install "auditable[graph-ad]"
|
|
372
|
+
python -c "import torch_geometric.typing as t; assert t.WITH_PYG_LIB"
|
|
373
|
+
```
|
|
374
|
+
|
|
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.
|
|
376
|
+
|
|
377
|
+
## Traces and Inputs
|
|
378
|
+
|
|
379
|
+
Methods read traces through `auditable.featurize.normalize_trace`, which accepts a list of step dicts, a run dict (`{"steps": ..., "texts": ...}`), a typed NetworkX graph from `auditable.graph.build_graph`, a `SessionGraph`, or the `TraceView` that `from_agentpile` returns. Steps carry `idx`, `agent`, `kind` (`"decision"` or `"tool_call"`), and optionally `deps` and `text`; a `SessionGraph` also keeps parallel branches and merges of the execution order. A trace without dependency annotations can be read with `dependency="full_context"`, the assumption that each step relied on every earlier step. A method that needs a field the trace lacks (step text for an LLM judge, for example) raises an error naming it.
|
|
380
|
+
|
|
381
|
+
`auditable.featurize` exposes the features the methods compute, so they can be inspected or reused: the GRADE structural layers (`grade_layer`, `grade_layer_matrix`), the Who&When step matrix (`who_when_step_matrix`), the step dependency graph and its statistics (`step_graph`, `span_series`, `run_max_span`), and graph encodings for PyGOD (`to_pyg`).
|
|
382
|
+
|
|
383
|
+
## Reproducing CatchBench
|
|
384
|
+
|
|
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.
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
export GRADE_DIR=/path/to/grade # checked out at 3839a57
|
|
389
|
+
export PYTHONHASHSEED=0
|
|
390
|
+
python benchmarks/catchbench_parity/run_parity.py checks # per-sample comparisons
|
|
391
|
+
python benchmarks/catchbench_parity/run_parity.py board --out board.txt # the full board through the library
|
|
392
|
+
python benchmarks/catchbench_parity/run_parity.py board --perturb --out perturbed.txt
|
|
393
|
+
python benchmarks/catchbench_parity/run_parity.py consumption board.txt perturbed.txt
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Adding a Method
|
|
397
|
+
|
|
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.
|
|
399
|
+
|
|
400
|
+
## Records and Graph Analysis
|
|
401
|
+
|
|
402
|
+
The package also contains the modules it was first built around. `audit` and `replay` capture an agent decision with the dependency state it relied on and re-decide it against later state; `analyze_run` ranks the steps of a finished run by structural blast share; `analyze_plan` lints a declared plan; `LiveSession` scores a growing run; and `auditable.integrations` captures observed dependency edges from LangGraph, LangChain, MCP servers, or any loop through `TouchRecorder`. See [`examples/README.md`](https://github.com/yzhao062/auditable/blob/main/examples/README.md) and the [documentation](https://auditable-ai.readthedocs.io/).
|
|
403
|
+
|
|
404
|
+
Records are content-addressed and hash-chained, and that property is limited. A signature or digest authenticates only the entries that were submitted, a hash chain exposes a later edit only when its head is anchored with an independent party, and neither reveals an event that was never written. The chain therefore does not establish that a log is complete.
|
|
405
|
+
|
|
406
|
+
### Legacy
|
|
407
|
+
|
|
408
|
+
`ActionGate`, `ReferenceLedger` (the in-process reference rail), and `DataAuditor` are deprecated as of 0.3 and emit a `DeprecationWarning` when constructed. They remain importable and will be removed in a later release. Tabular anomaly detection on dependency snapshots belongs in [PyOD](https://github.com/yzhao062/pyod).
|
|
409
|
+
|
|
410
|
+
## Related Projects
|
|
411
|
+
|
|
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.
|
|
414
|
+
- [GRADE](https://arxiv.org/abs/2606.22741): the typed two-layer graph of agent execution and dependency that the structural features read.
|
|
415
|
+
- [awesome-auditable-ai](https://github.com/yzhao062/awesome-auditable-ai): a reading list on AI agent reliability and auditing.
|
|
416
|
+
|
|
417
|
+
## Citation
|
|
418
|
+
|
|
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`):
|
|
432
|
+
|
|
433
|
+
```bibtex
|
|
434
|
+
@article{zhao2026grade,
|
|
435
|
+
title = {GRADE: Graph Representation of LLM Agent Dependency and Execution},
|
|
436
|
+
author = {Zhao, Yue},
|
|
437
|
+
journal = {arXiv preprint arXiv:2606.22741},
|
|
438
|
+
year = {2026}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@misc{zhao2026catchbench,
|
|
442
|
+
title = {{CatchBench}: When Can an Agent Failure Be Caught?},
|
|
443
|
+
author = {Yue Zhao},
|
|
444
|
+
year = {2026},
|
|
445
|
+
eprint = {2608.22808},
|
|
446
|
+
archivePrefix = {arXiv}
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## License
|
|
451
|
+
|
|
452
|
+
[Apache-2.0](https://github.com/yzhao062/auditable/blob/main/LICENSE). Ported code keeps the notice of its source (GRADE and CatchBench, both MIT License) at the top of each module.
|