evalrx 0.1.2__py3-none-any.whl

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.
Files changed (339) hide show
  1. evalrx/__init__.py +139 -0
  2. evalrx/agent_assets/__init__.py +2 -0
  3. evalrx/agent_assets/skills/README.md +28 -0
  4. evalrx/agent_assets/skills/eval-chart-style/SKILL.md +172 -0
  5. evalrx/agent_assets/skills/evalrx-report-ui/SKILL.md +116 -0
  6. evalrx/agent_assets/skills/nature-figure/LICENSE +201 -0
  7. evalrx/agent_assets/skills/nature-figure/README.md +412 -0
  8. evalrx/agent_assets/skills/nature-figure/SKILL.md +60 -0
  9. evalrx/agent_assets/skills/nature-figure/manifest.yaml +59 -0
  10. evalrx/agent_assets/skills/nature-figure/references/api.md +436 -0
  11. evalrx/agent_assets/skills/nature-figure/references/backend-selection.md +100 -0
  12. evalrx/agent_assets/skills/nature-figure/references/chart-types.md +281 -0
  13. evalrx/agent_assets/skills/nature-figure/references/common-patterns.md +350 -0
  14. evalrx/agent_assets/skills/nature-figure/references/demos.md +65 -0
  15. evalrx/agent_assets/skills/nature-figure/references/design-theory.md +439 -0
  16. evalrx/agent_assets/skills/nature-figure/references/figure-contract.md +93 -0
  17. evalrx/agent_assets/skills/nature-figure/references/figure-legend-conventions.md +71 -0
  18. evalrx/agent_assets/skills/nature-figure/references/nature-2026-observations.md +112 -0
  19. evalrx/agent_assets/skills/nature-figure/references/qa-contract.md +119 -0
  20. evalrx/agent_assets/skills/nature-figure/references/r-template-index.md +66 -0
  21. evalrx/agent_assets/skills/nature-figure/references/r-workflow.md +161 -0
  22. evalrx/agent_assets/skills/nature-figure/references/tutorials.md +251 -0
  23. evalrx/agent_assets/skills/nature-figure/static/core/contract.md +29 -0
  24. evalrx/agent_assets/skills/nature-figure/static/core/stance.md +37 -0
  25. evalrx/agent_assets/skills/nature-figure/static/fragments/backend/python.md +37 -0
  26. evalrx/agent_assets/skills/nature-figure/static/fragments/backend/r.md +44 -0
  27. evalrx/agent_assets/skills/outcome-driver-analysis/SKILL.md +213 -0
  28. evalrx/agent_assets/skills/outcome-driver-analysis/assets/analysis_report_template.md +53 -0
  29. evalrx/agent_assets/skills/outcome-driver-analysis/references/model_selection.md +72 -0
  30. evalrx/agent_assets/skills/outcome-driver-analysis/scripts/explanatory_var_eda.R +130 -0
  31. evalrx/agent_assets/skills/outcome-driver-analysis/scripts/explanatory_var_eda.py +150 -0
  32. evalrx/agent_assets/skills/outcome-driver-analysis/scripts/fit_outcome_model.R +181 -0
  33. evalrx/agent_assets/skills/outcome-driver-analysis/scripts/fit_outcome_model.py +186 -0
  34. evalrx/agent_assets/skills/outcome-driver-analysis/scripts/univariate_eda.R +149 -0
  35. evalrx/agent_assets/skills/outcome-driver-analysis/scripts/univariate_eda.py +177 -0
  36. evalrx/agent_assets/skills.py +27 -0
  37. evalrx/agent_runtime/__init__.py +78 -0
  38. evalrx/agent_runtime/_docker_runner.py +89 -0
  39. evalrx/agent_runtime/cli_runtime.py +103 -0
  40. evalrx/agent_runtime/cli_transcript.py +138 -0
  41. evalrx/agent_runtime/cli_types.py +68 -0
  42. evalrx/agent_runtime/codegen/__init__.py +5 -0
  43. evalrx/agent_runtime/codegen/runner.py +94 -0
  44. evalrx/agent_runtime/experiment_harness.py +117 -0
  45. evalrx/agent_runtime/factory.py +102 -0
  46. evalrx/agent_runtime/json_shape.py +44 -0
  47. evalrx/agent_runtime/judges/__init__.py +28 -0
  48. evalrx/agent_runtime/judges/agy.py +179 -0
  49. evalrx/agent_runtime/judges/autodetect.py +135 -0
  50. evalrx/agent_runtime/judges/claude.py +159 -0
  51. evalrx/agent_runtime/judges/codex.py +120 -0
  52. evalrx/agent_runtime/providers/__init__.py +21 -0
  53. evalrx/agent_runtime/providers/antigravity.py +31 -0
  54. evalrx/agent_runtime/providers/base.py +145 -0
  55. evalrx/agent_runtime/providers/claude_code.py +49 -0
  56. evalrx/agent_runtime/providers/codex.py +37 -0
  57. evalrx/agent_runtime/providers/gemini_cli.py +26 -0
  58. evalrx/agent_runtime/providers/kimi_cli.py +27 -0
  59. evalrx/agent_runtime/providers/opencode.py +27 -0
  60. evalrx/agent_runtime/providers/registry.py +58 -0
  61. evalrx/agent_runtime/sandbox.py +517 -0
  62. evalrx/agent_runtime/skill_audit.py +143 -0
  63. evalrx/agent_runtime/skills/__init__.py +19 -0
  64. evalrx/agent_runtime/skills/installer.py +68 -0
  65. evalrx/agent_runtime/skills/prompt_policy.py +86 -0
  66. evalrx/agent_runtime/skills/resolver.py +19 -0
  67. evalrx/analysis/__init__.py +132 -0
  68. evalrx/analysis/adjudicate.py +154 -0
  69. evalrx/analysis/analysis_module.py +361 -0
  70. evalrx/analysis/api.py +171 -0
  71. evalrx/analysis/case_studio.py +651 -0
  72. evalrx/analysis/cli.py +114 -0
  73. evalrx/analysis/dashboard.py +350 -0
  74. evalrx/analysis/eval_case_matrix.py +118 -0
  75. evalrx/analysis/eval_viz_theme.py +833 -0
  76. evalrx/analysis/explore_run.py +333 -0
  77. evalrx/analysis/explorer.py +1276 -0
  78. evalrx/analysis/failure_modes.py +607 -0
  79. evalrx/analysis/fused_pipeline.py +489 -0
  80. evalrx/analysis/holdout.py +300 -0
  81. evalrx/analysis/hypothesis_agent.py +230 -0
  82. evalrx/analysis/narration.py +177 -0
  83. evalrx/analysis/operationalize.py +442 -0
  84. evalrx/analysis/plain_language.py +42 -0
  85. evalrx/analysis/planner.py +283 -0
  86. evalrx/analysis/probe_search.py +203 -0
  87. evalrx/analysis/profile.py +268 -0
  88. evalrx/analysis/prompts/__init__.py +0 -0
  89. evalrx/analysis/prompts/explorer.py +417 -0
  90. evalrx/analysis/prompts/failure_modes.py +33 -0
  91. evalrx/analysis/prompts/holdout.py +27 -0
  92. evalrx/analysis/prompts/hypothesis_agent.py +78 -0
  93. evalrx/analysis/prompts/run_codebase.py +47 -0
  94. evalrx/analysis/prompts/stats_agent.py +72 -0
  95. evalrx/analysis/prompts/stats_tool_generator.py +43 -0
  96. evalrx/analysis/result_marker.py +47 -0
  97. evalrx/analysis/run_codebase.py +242 -0
  98. evalrx/analysis/run_view.py +205 -0
  99. evalrx/analysis/stage_views.py +93 -0
  100. evalrx/analysis/stats_agent.py +944 -0
  101. evalrx/analysis/stats_tool_agent.py +261 -0
  102. evalrx/analysis/stats_tool_generator.py +415 -0
  103. evalrx/analysis/stats_tools.py +1153 -0
  104. evalrx/analysis/trajectory_records.py +193 -0
  105. evalrx/analysis/workbench.py +431 -0
  106. evalrx/analyzers/__init__.py +42 -0
  107. evalrx/analyzers/agent/__init__.py +25 -0
  108. evalrx/analyzers/agent/counterfactual.py +84 -0
  109. evalrx/analyzers/agent/first_error_judge.py +96 -0
  110. evalrx/analyzers/agent/ignored_obs.py +81 -0
  111. evalrx/analyzers/agent/loop_detect.py +79 -0
  112. evalrx/analyzers/agent/reliability.py +165 -0
  113. evalrx/analyzers/agent/tool_shap.py +225 -0
  114. evalrx/analyzers/agent/trajectory_rubric.py +168 -0
  115. evalrx/analyzers/attention/__init__.py +19 -0
  116. evalrx/analyzers/attention/relative_attn.py +610 -0
  117. evalrx/analyzers/attention/rollout.py +73 -0
  118. evalrx/analyzers/attention/sink.py +56 -0
  119. evalrx/analyzers/attention/summary.py +190 -0
  120. evalrx/analyzers/attribution/__init__.py +6 -0
  121. evalrx/analyzers/attribution/generic_attn.py +31 -0
  122. evalrx/analyzers/attribution/gradcam.py +30 -0
  123. evalrx/analyzers/base.py +12 -0
  124. evalrx/analyzers/geometry/__init__.py +6 -0
  125. evalrx/analyzers/geometry/cka.py +70 -0
  126. evalrx/analyzers/geometry/linear_probe.py +157 -0
  127. evalrx/analyzers/hallucination/__init__.py +9 -0
  128. evalrx/analyzers/hallucination/chair.py +78 -0
  129. evalrx/analyzers/hallucination/opera.py +29 -0
  130. evalrx/analyzers/hallucination/pope.py +119 -0
  131. evalrx/analyzers/hallucination/selfcheck.py +155 -0
  132. evalrx/analyzers/hallucination/vcd.py +29 -0
  133. evalrx/analyzers/lens/__init__.py +7 -0
  134. evalrx/analyzers/lens/layer_contrast.py +133 -0
  135. evalrx/analyzers/lens/logit_lens.py +138 -0
  136. evalrx/analyzers/lens/tuned_lens.py +30 -0
  137. evalrx/analyzers/patching/__init__.py +5 -0
  138. evalrx/analyzers/patching/causal_trace.py +30 -0
  139. evalrx/analyzers/perturbation/__init__.py +23 -0
  140. evalrx/analyzers/perturbation/_shapley.py +54 -0
  141. evalrx/analyzers/perturbation/context_shap.py +174 -0
  142. evalrx/analyzers/perturbation/cot_faithfulness.py +239 -0
  143. evalrx/analyzers/perturbation/format_sensitivity.py +237 -0
  144. evalrx/analyzers/perturbation/mm_shap.py +146 -0
  145. evalrx/analyzers/perturbation/modality_ablation.py +196 -0
  146. evalrx/analyzers/perturbation/perturbation_battery.py +274 -0
  147. evalrx/analyzers/perturbation/prompt_contrast.py +265 -0
  148. evalrx/analyzers/perturbation/rise.py +94 -0
  149. evalrx/analyzers/perturbation/vl_shap.py +102 -0
  150. evalrx/analyzers/reasoning/__init__.py +33 -0
  151. evalrx/analyzers/reasoning/_text.py +328 -0
  152. evalrx/analyzers/reasoning/answer_extraction_audit.py +327 -0
  153. evalrx/analyzers/reasoning/arith_audit.py +226 -0
  154. evalrx/analyzers/reasoning/contamination.py +214 -0
  155. evalrx/analyzers/reasoning/knowledge_split.py +253 -0
  156. evalrx/analyzers/reasoning/self_repair.py +246 -0
  157. evalrx/analyzers/reasoning/step_rollout_value.py +216 -0
  158. evalrx/analyzers/reasoning/termination_audit.py +258 -0
  159. evalrx/analyzers/uncertainty/__init__.py +18 -0
  160. evalrx/analyzers/uncertainty/calibration.py +174 -0
  161. evalrx/analyzers/uncertainty/coverage_gap.py +199 -0
  162. evalrx/analyzers/uncertainty/entropy.py +90 -0
  163. evalrx/analyzers/uncertainty/logprob_entropy.py +69 -0
  164. evalrx/analyzers/uncertainty/self_consistency.py +204 -0
  165. evalrx/analyzers/uncertainty/verbalized_conf.py +64 -0
  166. evalrx/cli.py +411 -0
  167. evalrx/config.py +77 -0
  168. evalrx/contract/__init__.py +179 -0
  169. evalrx/contract/common.py +452 -0
  170. evalrx/contract/emit.py +948 -0
  171. evalrx/contract/export.py +237 -0
  172. evalrx/contract/m1.py +325 -0
  173. evalrx/contract/m2.py +317 -0
  174. evalrx/contract/m3.py +165 -0
  175. evalrx/contract/m4.py +130 -0
  176. evalrx/contract/m5.py +292 -0
  177. evalrx/contract/methodology.py +76 -0
  178. evalrx/contract/pre_m1.py +58 -0
  179. evalrx/contract/typescript.py +140 -0
  180. evalrx/core/__init__.py +85 -0
  181. evalrx/core/analyzer.py +174 -0
  182. evalrx/core/capability.py +54 -0
  183. evalrx/core/case.py +443 -0
  184. evalrx/core/experiment.py +106 -0
  185. evalrx/core/model.py +198 -0
  186. evalrx/core/pipeline.py +42 -0
  187. evalrx/core/registry.py +142 -0
  188. evalrx/core/result.py +64 -0
  189. evalrx/core/spec.py +173 -0
  190. evalrx/core/tokentype.py +165 -0
  191. evalrx/core/tool.py +92 -0
  192. evalrx/datasets/__init__.py +41 -0
  193. evalrx/datasets/base.py +68 -0
  194. evalrx/datasets/gui_os.py +52 -0
  195. evalrx/datasets/llm_qa.py +57 -0
  196. evalrx/datasets/pure_qa.py +12 -0
  197. evalrx/datasets/vlm_qa.py +695 -0
  198. evalrx/datasets/web_search_qa.py +52 -0
  199. evalrx/eval_agent/__init__.py +341 -0
  200. evalrx/eval_agent/_tools.py +81 -0
  201. evalrx/eval_agent/ab_runner.py +50 -0
  202. evalrx/eval_agent/agentic/__init__.py +43 -0
  203. evalrx/eval_agent/agentic/actions.py +216 -0
  204. evalrx/eval_agent/agentic/board.py +107 -0
  205. evalrx/eval_agent/agentic/loop.py +190 -0
  206. evalrx/eval_agent/agentic/tools.py +538 -0
  207. evalrx/eval_agent/checkpoint.py +57 -0
  208. evalrx/eval_agent/cli_agent.py +59 -0
  209. evalrx/eval_agent/cli_skills.py +5 -0
  210. evalrx/eval_agent/evolution.py +396 -0
  211. evalrx/eval_agent/git_manager.py +215 -0
  212. evalrx/eval_agent/hypothesis.py +172 -0
  213. evalrx/eval_agent/label_quarantine.py +209 -0
  214. evalrx/eval_agent/legacy.py +530 -0
  215. evalrx/eval_agent/log_schema.py +497 -0
  216. evalrx/eval_agent/loop.py +2159 -0
  217. evalrx/eval_agent/loop_reports.py +116 -0
  218. evalrx/eval_agent/model_instrumentation.py +282 -0
  219. evalrx/eval_agent/narration.py +193 -0
  220. evalrx/eval_agent/nl_runner.py +460 -0
  221. evalrx/eval_agent/orchestrator.py +61 -0
  222. evalrx/eval_agent/preregister.py +93 -0
  223. evalrx/eval_agent/prompts/__init__.py +1 -0
  224. evalrx/eval_agent/prompts/agentic.py +46 -0
  225. evalrx/eval_agent/prompts/case_discovery.py +25 -0
  226. evalrx/eval_agent/prompts/diagnosis.py +125 -0
  227. evalrx/eval_agent/prompts/experiment_writer.py +265 -0
  228. evalrx/eval_agent/prompts/explore_step.py +37 -0
  229. evalrx/eval_agent/prompts/fix_agent.py +257 -0
  230. evalrx/eval_agent/prompts/hypothesis_tester.py +15 -0
  231. evalrx/eval_agent/prompts/nl_runner.py +38 -0
  232. evalrx/eval_agent/prompts/probe_agent.py +25 -0
  233. evalrx/eval_agent/prompts/probe_candidate_generator.py +14 -0
  234. evalrx/eval_agent/prompts/probe_generator.py +35 -0
  235. evalrx/eval_agent/prompts/whitebox_probe_generator.py +38 -0
  236. evalrx/eval_agent/report.py +58 -0
  237. evalrx/eval_agent/run_context.py +354 -0
  238. evalrx/eval_agent/run_log.schema.json +1215 -0
  239. evalrx/eval_agent/run_logger_v2.py +1764 -0
  240. evalrx/eval_agent/run_metadata.py +208 -0
  241. evalrx/eval_agent/stages/__init__.py +56 -0
  242. evalrx/eval_agent/stages/case_discovery.py +293 -0
  243. evalrx/eval_agent/stages/diagnosis.py +1017 -0
  244. evalrx/eval_agent/stages/experiment_writer.py +1634 -0
  245. evalrx/eval_agent/stages/fix_agent.py +3916 -0
  246. evalrx/eval_agent/stages/fix_internals.py +499 -0
  247. evalrx/eval_agent/stages/fix_pipeline.py +725 -0
  248. evalrx/eval_agent/stages/fix_tiers.py +187 -0
  249. evalrx/eval_agent/stages/fix_tools.py +1034 -0
  250. evalrx/eval_agent/stages/hypothesis_tester.py +1014 -0
  251. evalrx/eval_agent/stages/probe.py +439 -0
  252. evalrx/eval_agent/stages/probe_agent.py +1079 -0
  253. evalrx/eval_agent/stages/probe_candidate_generator.py +128 -0
  254. evalrx/eval_agent/stages/probe_generator.py +326 -0
  255. evalrx/eval_agent/stages/probe_search_agent.py +106 -0
  256. evalrx/eval_agent/stages/protocol.py +112 -0
  257. evalrx/eval_agent/stages/repair_catalog.py +273 -0
  258. evalrx/eval_agent/stages/surgery.py +524 -0
  259. evalrx/eval_agent/stages/whitebox_probe_generator.py +351 -0
  260. evalrx/eval_agent/store.py +231 -0
  261. evalrx/logging_utils.py +112 -0
  262. evalrx/models/__init__.py +161 -0
  263. evalrx/models/_discover.py +101 -0
  264. evalrx/models/agent.py +380 -0
  265. evalrx/models/backends/__init__.py +58 -0
  266. evalrx/models/backends/api.py +169 -0
  267. evalrx/models/backends/base.py +57 -0
  268. evalrx/models/backends/gemini_compat.py +579 -0
  269. evalrx/models/backends/hf_local.py +2074 -0
  270. evalrx/models/backends/openai_compat.py +301 -0
  271. evalrx/models/backends/vllm_offline.py +116 -0
  272. evalrx/models/base.py +24 -0
  273. evalrx/models/blackbox/__init__.py +4 -0
  274. evalrx/models/blackbox/agent.py +31 -0
  275. evalrx/models/blackbox/base.py +29 -0
  276. evalrx/models/blackbox/gemini.py +279 -0
  277. evalrx/models/blackbox/llm_api.py +17 -0
  278. evalrx/models/blackbox/vlm_api.py +17 -0
  279. evalrx/models/compose.py +66 -0
  280. evalrx/models/inference.py +88 -0
  281. evalrx/models/paper_methods/__init__.py +8 -0
  282. evalrx/models/paper_methods/aad.py +53 -0
  283. evalrx/models/paper_methods/ifcd.py +204 -0
  284. evalrx/models/paper_methods/pai.py +164 -0
  285. evalrx/models/paper_methods/tcd.py +202 -0
  286. evalrx/models/paper_methods/vcd.py +45 -0
  287. evalrx/models/paper_methods/vicrop.py +137 -0
  288. evalrx/models/toolcodec.py +143 -0
  289. evalrx/models/tools/__init__.py +20 -0
  290. evalrx/models/tools/perception.py +300 -0
  291. evalrx/models/tools/visual.py +174 -0
  292. evalrx/models/whitebox/__init__.py +26 -0
  293. evalrx/models/whitebox/agent.py +31 -0
  294. evalrx/models/whitebox/base.py +24 -0
  295. evalrx/models/whitebox/qwen.py +61 -0
  296. evalrx/models/whitebox/qwen2_5_omni.py +29 -0
  297. evalrx/models/whitebox/qwen2_audio.py +25 -0
  298. evalrx/models/whitebox/qwen_omni.py +53 -0
  299. evalrx/models/whitebox/qwen_vl.py +62 -0
  300. evalrx/observability/__init__.py +21 -0
  301. evalrx/observability/envelope.py +122 -0
  302. evalrx/observability/outbox.py +111 -0
  303. evalrx/observability/tracer.py +882 -0
  304. evalrx/reporting/__init__.py +28 -0
  305. evalrx/reporting/case_study.py +947 -0
  306. evalrx/reporting/compiler.py +587 -0
  307. evalrx/reporting/dynamic.py +1882 -0
  308. evalrx/reporting/html_report.py +2225 -0
  309. evalrx/reporting/langfuse_exporter.py +38 -0
  310. evalrx/reporting/langfuse_source.py +155 -0
  311. evalrx/reporting/model.py +151 -0
  312. evalrx/reporting/run_events.py +184 -0
  313. evalrx/reporting/server.py +557 -0
  314. evalrx/reporting/stages.py +58 -0
  315. evalrx/reporting/static_export.py +142 -0
  316. evalrx/reporting/web_dist/index.html +146 -0
  317. evalrx/specs.py +727 -0
  318. evalrx/stats/__init__.py +47 -0
  319. evalrx/stats/api.py +192 -0
  320. evalrx/stats/bootstrap.py +86 -0
  321. evalrx/stats/ebh.py +27 -0
  322. evalrx/stats/evalue.py +98 -0
  323. evalrx/stats/friedman.py +138 -0
  324. evalrx/stats/mcnemar.py +40 -0
  325. evalrx/stats/multiplicity.py +159 -0
  326. evalrx/stats/subset_sampling.py +55 -0
  327. evalrx/term_links.py +43 -0
  328. evalrx/viz/__init__.py +7 -0
  329. evalrx/viz/labels.py +77 -0
  330. evalrx/viz/prompts.py +39 -0
  331. evalrx/viz/renderer.py +590 -0
  332. evalrx/viz/schema.py +36 -0
  333. evalrx/viz/style.py +134 -0
  334. evalrx-0.1.2.dist-info/METADATA +532 -0
  335. evalrx-0.1.2.dist-info/RECORD +339 -0
  336. evalrx-0.1.2.dist-info/WHEEL +5 -0
  337. evalrx-0.1.2.dist-info/entry_points.txt +3 -0
  338. evalrx-0.1.2.dist-info/licenses/LICENSE +121 -0
  339. evalrx-0.1.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,538 @@
1
+ """Tool registry for the agentic diagnosis loop.
2
+
3
+ Each :class:`ToolSpec` wraps one existing stage (M1 probe, M2 stats, M3
4
+ diagnosis, M4 test, ...) behind a name/schema the judge can call. The host —
5
+ not the judge — enforces call caps, preconditions, and the pre-registration
6
+ discipline (no declaring success without a statistically supported,
7
+ protocol-consistent hypothesis).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+ from typing import TYPE_CHECKING, Any, Callable
14
+
15
+ if TYPE_CHECKING:
16
+ from evalrx.eval_agent.agentic.actions import Action
17
+ from evalrx.eval_agent.agentic.board import EvidenceBoard
18
+ from evalrx.eval_agent.loop_reports import VLDiagnoseReport
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class ToolSpec:
23
+ """One tool the judge may call."""
24
+
25
+ name: str
26
+ description: str
27
+ params_schema: dict[str, Any]
28
+ handler: "Callable[[EvidenceBoard, dict[str, Any]], ToolOutcome]"
29
+ max_calls: int = 3
30
+ requires: tuple[str, ...] = ()
31
+
32
+
33
+ @dataclass
34
+ class ToolOutcome:
35
+ """What happened when a tool ran — the summary is what re-enters the prompt."""
36
+
37
+ ok: bool
38
+ summary: str
39
+ payload: Any = None
40
+ error: str | None = None
41
+
42
+
43
+ # Precondition names -> board attribute truthiness checks.
44
+ _PRECONDITIONS: dict[str, "Callable[[EvidenceBoard], bool]"] = {
45
+ "probe_findings": lambda b: bool(b.probe_findings),
46
+ "stats_findings": lambda b: bool(b.stats_findings),
47
+ "stats_confirmatory": lambda b: bool(b.stats_confirmatory),
48
+ "hypotheses": lambda b: bool(b.hypotheses),
49
+ "supported_hypothesis": lambda b: b.has_supported_hypothesis(),
50
+ }
51
+
52
+
53
+ class ToolRegistry:
54
+ """Holds the tools available to the judge for one agentic run."""
55
+
56
+ def __init__(self) -> None:
57
+ self._specs: dict[str, ToolSpec] = {}
58
+
59
+ def register(self, spec: ToolSpec) -> None:
60
+ self._specs[spec.name] = spec
61
+
62
+ def tool_names(self) -> set[str]:
63
+ return set(self._specs)
64
+
65
+ def get(self, name: str) -> ToolSpec:
66
+ return self._specs[name]
67
+
68
+ def catalog_for_prompt(self, board: "EvidenceBoard") -> str:
69
+ lines = []
70
+ for spec in self._specs.values():
71
+ calls_left = (
72
+ spec.max_calls - board.calls_made(spec.name)
73
+ if spec.max_calls > 0
74
+ else None
75
+ )
76
+ unmet = [r for r in spec.requires if not _PRECONDITIONS[r](board)]
77
+ bits = [f"- {spec.name}: {spec.description}"]
78
+ if calls_left is not None:
79
+ bits.append(f"(calls left: {calls_left})")
80
+ if unmet:
81
+ bits.append(f"[BLOCKED until: {', '.join(unmet)}]")
82
+ lines.append(" ".join(bits))
83
+ return "\n".join(lines)
84
+
85
+ def dispatch(self, action: "Action", board: "EvidenceBoard") -> ToolOutcome:
86
+ spec = self._specs.get(action.tool)
87
+ if spec is None:
88
+ return ToolOutcome(False, f"unknown tool {action.tool!r}", error="unknown_tool")
89
+
90
+ if spec.max_calls > 0 and board.calls_made(spec.name) >= spec.max_calls:
91
+ return ToolOutcome(
92
+ False,
93
+ f"{spec.name} has reached its call limit ({spec.max_calls})",
94
+ error="max_calls",
95
+ )
96
+
97
+ unmet = [r for r in spec.requires if not _PRECONDITIONS[r](board)]
98
+ if unmet:
99
+ return ToolOutcome(
100
+ False,
101
+ f"{spec.name} requires {unmet} first",
102
+ error="precondition",
103
+ )
104
+
105
+ if spec.name == "stop" and action.params.get("resolved") and not board.has_supported_hypothesis():
106
+ return ToolOutcome(
107
+ False,
108
+ "cannot declare success: no statistically supported, "
109
+ "protocol-consistent hypothesis yet — call test_hypothesis first",
110
+ error="no_supported_hypothesis",
111
+ )
112
+
113
+ return spec.handler(board, action.params)
114
+
115
+
116
+ # ---------------------------------------------------------------------------
117
+ # Default registry: wraps the same M1-M4 stages VLDiagnoseLoop calls directly.
118
+ # ---------------------------------------------------------------------------
119
+
120
+
121
+ @dataclass
122
+ class _RunState:
123
+ """Mutable, per-run state the default tool handlers read and update.
124
+
125
+ Mirrors the local variables ``VLDiagnoseLoop.run()`` keeps on its stack —
126
+ an agentic run just spreads the same bookkeeping across tool calls instead
127
+ of a fixed cycle.
128
+ """
129
+
130
+ original_data: Any
131
+ data: Any
132
+ cycle: int = 0
133
+ timings: dict[str, float] = field(default_factory=dict)
134
+ all_hypotheses: list[Any] = field(default_factory=list)
135
+ pending_hypotheses: list[Any] = field(default_factory=list)
136
+ all_test_results: list[Any] = field(default_factory=list)
137
+ prior_cycles: list[dict[str, Any]] = field(default_factory=list)
138
+ probe_results: dict[str, Any] = field(default_factory=dict)
139
+ artifact_pngs: list[Any] = field(default_factory=list)
140
+ stats_report: Any = None
141
+ failure_modes: Any = None
142
+ probe_search_result: Any = None
143
+ surgery_outcome: Any = None
144
+ fix_outcome: Any = None
145
+
146
+
147
+ def _summarize_probe(probe_results: dict[str, Any]) -> list[dict[str, Any]]:
148
+ """Compact, board-friendly summary of one M1 pass (scalars only, no per-case rows)."""
149
+ out = []
150
+ for name, res in probe_results.items():
151
+ findings = res.findings or {}
152
+ scalars = {k: v for k, v in findings.items() if isinstance(v, (int, float, bool, str))}
153
+ out.append({
154
+ "analyzer": name,
155
+ "scalars": scalars,
156
+ "n_per_case": len(findings.get("per_case", []) or []),
157
+ })
158
+ return out
159
+
160
+
161
+ def _summarize_stats(report: Any) -> list[dict[str, Any]]:
162
+ results = getattr(report, "stats_results", None) or []
163
+ return [
164
+ {
165
+ "tool": r.tool, "ok": r.ok, "effect": r.effect,
166
+ "reject": r.reject, "e_value": r.e_value, "summary": r.summary,
167
+ }
168
+ for r in results
169
+ ]
170
+
171
+
172
+ def _current_report(loop: Any, state: _RunState) -> "VLDiagnoseReport":
173
+ """Snapshot the run so far into the same report shape run_m5/run_fix expect."""
174
+ from evalrx.eval_agent.loop_reports import VLDiagnoseReport
175
+
176
+ verified = loop.hypothesis_tester.best_hypotheses(state.all_test_results)
177
+ return VLDiagnoseReport(
178
+ cycles=state.cycle,
179
+ resolved=bool(verified),
180
+ stopped_by="in_progress",
181
+ verified_hypotheses=verified,
182
+ final_hypotheses=state.all_hypotheses,
183
+ all_test_results=state.all_test_results,
184
+ final_stats_report=state.stats_report,
185
+ store=loop.store,
186
+ _run_id=getattr(loop, "_run_id", ""),
187
+ )
188
+
189
+
190
+ def _run_probe(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
191
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
192
+ probe_results, artifact_pngs = loop._do_m1(
193
+ state.cycle, state.data, state.all_hypotheses, state.timings
194
+ )
195
+ if not probe_results:
196
+ return ToolOutcome(False, "M1 produced no probe results", error="empty")
197
+ state.probe_results = probe_results
198
+ state.artifact_pngs = artifact_pngs
199
+ board.probe_findings = _summarize_probe(probe_results)
200
+ summary = f"ran {len(probe_results)} analyzer(s): {sorted(probe_results)}"
201
+ state.cycle += 1
202
+ return ToolOutcome(True, summary, payload=probe_results)
203
+
204
+ return handler
205
+
206
+
207
+ def _run_stats(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
208
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
209
+ confirmatory = bool(params.get("confirmatory", True))
210
+ stats_report = loop._do_m2(
211
+ state.cycle, state.probe_results, state.data, state.artifact_pngs,
212
+ state.timings, confirmatory=confirmatory,
213
+ )
214
+ state.stats_report = stats_report
215
+ board.stats_findings = _summarize_stats(stats_report)
216
+ board.stats_confirmatory = confirmatory and not getattr(
217
+ stats_report, "descriptive_only", False
218
+ )
219
+ return ToolOutcome(
220
+ True,
221
+ f"severity={stats_report.severity}, confirmatory={board.stats_confirmatory}",
222
+ payload=stats_report,
223
+ )
224
+
225
+ return handler
226
+
227
+
228
+ def _explore_data(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
229
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
230
+ from evalrx.analysis.operationalize import per_case_to_records
231
+ from evalrx.analysis.stats_tools import build_stats_input
232
+
233
+ question = str(
234
+ params.get("question")
235
+ or "Explore this dataset and surface the patterns that matter."
236
+ )
237
+ stats_input = build_stats_input(state.probe_results, state.data)
238
+ records = per_case_to_records(stats_input.per_case, stats_input.labels)
239
+ if not records:
240
+ return ToolOutcome(False, "no per-case records to explore yet", error="empty")
241
+ report = loop.explorer.explore_records(records, question=question)
242
+ board.explore_takeaways = [t.to_dict() for t in getattr(report, "takeaways", [])]
243
+ summary = (
244
+ f"{len(board.explore_takeaways)} takeaway(s)"
245
+ if report.ok else (report.error or "explore failed")
246
+ )
247
+ return ToolOutcome(bool(report.ok), summary, payload=report)
248
+
249
+ return handler
250
+
251
+
252
+ def _cluster_failures(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
253
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
254
+ from evalrx.analysis.failure_modes import cluster_failures
255
+ from evalrx.analysis.operationalize import per_case_to_records
256
+ from evalrx.analysis.stats_tools import build_stats_input
257
+
258
+ stats_input = build_stats_input(state.probe_results, state.data)
259
+ records = per_case_to_records(stats_input.per_case, stats_input.labels)
260
+ if not records:
261
+ return ToolOutcome(False, "no per-case records to cluster yet", error="empty")
262
+ report = cluster_failures(
263
+ records, # per_case_to_records writes label="fail"/"pass" — matches the defaults
264
+ min_cluster_size=int(params.get("min_cluster_size", 3)),
265
+ max_clusters=int(params.get("max_clusters", 8)),
266
+ )
267
+ state.failure_modes = report
268
+ board.failure_modes = [c.to_dict() for c in report.clusters]
269
+ run_logger = getattr(loop, "run_logger", None)
270
+ if run_logger is not None:
271
+ run_logger.save_artifact_json("failure_modes.json", report.to_dict())
272
+ return ToolOutcome(
273
+ True, f"{len(report.clusters)} failure mode(s) from {report.n_fail_cases} FAIL case(s)",
274
+ payload=report,
275
+ )
276
+
277
+ return handler
278
+
279
+
280
+ def _search_probes(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
281
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
282
+ from evalrx.eval_agent.stages.probe_search_agent import ProbeSearchAgent
283
+
284
+ seed_pool = state.data
285
+ if not len(seed_pool):
286
+ return ToolOutcome(False, "no seed cases available to probe", error="empty")
287
+ budget = int(params.get("budget", 10))
288
+ agent = ProbeSearchAgent(
289
+ judge=loop.judge, protocol=loop.protocol, budget=budget,
290
+ run_logger=getattr(loop, "run_logger", None),
291
+ )
292
+ result = agent.run(loop.model, seed_pool)
293
+ state.probe_search_result = result
294
+ board.probe_search_findings = [
295
+ {
296
+ "prompt": c.inputs.prompt, "expected": c.expected, "observed": str(c.observed),
297
+ }
298
+ for c in result.failure_cases
299
+ ]
300
+ run_logger = getattr(loop, "run_logger", None)
301
+ if run_logger is not None:
302
+ run_logger.save_artifact_json("probe_search_result.json", result.to_dict())
303
+ summary = (
304
+ f"{result.n_simulations} simulation(s) (macro={result.n_macro}, "
305
+ f"micro={result.n_micro}), {len(result.failure_cases)} new failure(s) "
306
+ f"found (error_rate={result.error_rate:.0%})"
307
+ )
308
+ return ToolOutcome(bool(result.n_simulations), summary, payload=result)
309
+
310
+ return handler
311
+
312
+
313
+ def _propose_hypotheses(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
314
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
315
+ diag = loop._do_m3(
316
+ state.cycle, state.stats_report, state.prior_cycles, state.timings,
317
+ failure_modes=state.failure_modes,
318
+ )
319
+ if diag is None or not diag.hypotheses:
320
+ return ToolOutcome(False, "M3 produced no hypotheses", error="empty")
321
+ for h in diag.hypotheses:
322
+ loop.store.add_hypothesis(h)
323
+ state.all_hypotheses.extend(diag.hypotheses)
324
+ state.pending_hypotheses = list(diag.hypotheses)
325
+ board.hypotheses = [
326
+ {
327
+ "statement": h.statement,
328
+ "failure_mode": h.predicted_failure_mode,
329
+ "status": h.status.value if h.status else "proposed",
330
+ }
331
+ for h in state.all_hypotheses
332
+ ]
333
+ state.prior_cycles.append({
334
+ "cycle": state.cycle,
335
+ "severity": getattr(state.stats_report, "severity", "none"),
336
+ "hypotheses": [
337
+ {
338
+ "statement": h.statement,
339
+ "failure_mode": h.predicted_failure_mode,
340
+ "status": h.status.value if h.status else "pending",
341
+ }
342
+ for h in diag.hypotheses
343
+ ],
344
+ })
345
+ return ToolOutcome(True, f"proposed {len(diag.hypotheses)} hypothesis(es)", payload=diag)
346
+
347
+ return handler
348
+
349
+
350
+ def _test_hypothesis(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
351
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
352
+ if not state.pending_hypotheses:
353
+ return ToolOutcome(
354
+ False, "no untested hypotheses — call propose_hypotheses first", error="empty"
355
+ )
356
+ loop._finalize_confirmatory_stats(state.stats_report)
357
+ board.stats_confirmatory = True
358
+ test_results = loop._do_m4(
359
+ state.cycle, state.pending_hypotheses, state.stats_report, state.data, state.timings
360
+ )
361
+ for tr in test_results:
362
+ tr.hypothesis.status = tr.status
363
+ state.all_test_results.extend(test_results)
364
+ state.pending_hypotheses = []
365
+ board.hypotheses = [
366
+ {
367
+ "statement": tr.hypothesis.statement,
368
+ "failure_mode": tr.hypothesis.predicted_failure_mode,
369
+ "status": tr.status.value,
370
+ "is_consistent_with_protocol": tr.is_consistent_with_protocol,
371
+ "effect_size": tr.effect_size,
372
+ "confidence": tr.confidence,
373
+ }
374
+ for tr in state.all_test_results
375
+ ]
376
+ n_supported = sum(1 for tr in test_results if tr.status.value == "supported")
377
+ return ToolOutcome(
378
+ True, f"tested {len(test_results)} hypothesis(es), {n_supported} supported",
379
+ payload=test_results,
380
+ )
381
+
382
+ return handler
383
+
384
+
385
+ def _run_surgery(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
386
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
387
+ report = _current_report(loop, state)
388
+ iv = loop.run_m5(report, state.original_data)
389
+ state.surgery_outcome = iv
390
+ if iv is None:
391
+ return ToolOutcome(False, "surgery produced no result", error="empty")
392
+ return ToolOutcome(True, f"surgery status={iv.status.value} fixed={iv.fixed}", payload=iv)
393
+
394
+ return handler
395
+
396
+
397
+ def _run_fix(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
398
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
399
+ report = _current_report(loop, state)
400
+ outcome = loop.run_fix(report, state.original_data)
401
+ state.fix_outcome = outcome
402
+ if outcome is None:
403
+ return ToolOutcome(False, "fix produced no result", error="empty")
404
+ fixed = bool(getattr(outcome, "fixed", False))
405
+ return ToolOutcome(True, f"fix fixed={fixed}", payload=outcome)
406
+
407
+ return handler
408
+
409
+
410
+ def _stop(loop: Any, state: _RunState) -> "Callable[[EvidenceBoard, dict], ToolOutcome]":
411
+ def handler(board: "EvidenceBoard", params: dict[str, Any]) -> ToolOutcome:
412
+ resolved = bool(params.get("resolved", False))
413
+ reason = str(params.get("reason") or "")
414
+ return ToolOutcome(True, f"stop(resolved={resolved}): {reason}".strip())
415
+
416
+ return handler
417
+
418
+
419
+ def build_default_registry(loop: Any, state: _RunState) -> ToolRegistry:
420
+ """Build the default M1-M4 tool registry for one :class:`AgenticDiagnoseLoop` run."""
421
+ registry = ToolRegistry()
422
+ registry.register(ToolSpec(
423
+ name="run_probe",
424
+ description="Run M1 analyzer probing to gather per-case findings on the model.",
425
+ params_schema={"type": "object", "properties": {}},
426
+ handler=_run_probe(loop, state),
427
+ max_calls=3,
428
+ ))
429
+ registry.register(ToolSpec(
430
+ name="run_stats",
431
+ description=(
432
+ "Run M2 statistical analysis over the probe findings gathered so far. "
433
+ "params.confirmatory (bool, default true): apply e-BH FDR correction now "
434
+ "vs. defer it (descriptive-only pass)."
435
+ ),
436
+ params_schema={
437
+ "type": "object",
438
+ "properties": {"confirmatory": {"type": "boolean"}},
439
+ },
440
+ handler=_run_stats(loop, state),
441
+ max_calls=3,
442
+ requires=("probe_findings",),
443
+ ))
444
+ if getattr(loop, "explorer", None) is not None:
445
+ registry.register(ToolSpec(
446
+ name="explore_data",
447
+ description=(
448
+ "Run free-form exploratory data analysis over the per-case signals "
449
+ "gathered so far. params.question (str, optional): what to look for."
450
+ ),
451
+ params_schema={
452
+ "type": "object",
453
+ "properties": {"question": {"type": "string"}},
454
+ },
455
+ handler=_explore_data(loop, state),
456
+ max_calls=2,
457
+ requires=("probe_findings",),
458
+ ))
459
+ registry.register(ToolSpec(
460
+ name="cluster_failures",
461
+ description=(
462
+ "Cluster FAIL cases into interpretable failure modes from the per-case "
463
+ "signals gathered so far — surfaces patterns propose_hypotheses can draw "
464
+ "on. params.min_cluster_size (int, default 3), params.max_clusters (int, default 8)."
465
+ ),
466
+ params_schema={
467
+ "type": "object",
468
+ "properties": {
469
+ "min_cluster_size": {"type": "integer"},
470
+ "max_clusters": {"type": "integer"},
471
+ },
472
+ },
473
+ handler=_cluster_failures(loop, state),
474
+ max_calls=2,
475
+ requires=("probe_findings",),
476
+ ))
477
+ registry.register(ToolSpec(
478
+ name="search_probes",
479
+ description=(
480
+ "Run a hierarchical Macro/Micro MCTS probe search (ProbeLLM-style) that "
481
+ "synthesizes and evaluates NEW test cases beyond the loaded dataset, "
482
+ "seeded from it — surfaces failures the original data never showed. "
483
+ "params.budget (int, default 10): total new probes to simulate."
484
+ ),
485
+ params_schema={"type": "object", "properties": {"budget": {"type": "integer"}}},
486
+ handler=_search_probes(loop, state),
487
+ max_calls=2,
488
+ ))
489
+ registry.register(ToolSpec(
490
+ name="propose_hypotheses",
491
+ description="Run M3 to propose falsifiable hypotheses from the stats findings so far.",
492
+ params_schema={"type": "object", "properties": {}},
493
+ handler=_propose_hypotheses(loop, state),
494
+ max_calls=4,
495
+ requires=("stats_findings",),
496
+ ))
497
+ registry.register(ToolSpec(
498
+ name="test_hypothesis",
499
+ description="Run M4 to statistically test the most recently proposed hypotheses.",
500
+ params_schema={"type": "object", "properties": {}},
501
+ handler=_test_hypothesis(loop, state),
502
+ max_calls=4,
503
+ requires=("hypotheses",),
504
+ ))
505
+ registry.register(ToolSpec(
506
+ name="run_surgery",
507
+ description="Propose a mechanistic fix (M5) for the best verified hypothesis.",
508
+ params_schema={"type": "object", "properties": {}},
509
+ handler=_run_surgery(loop, state),
510
+ max_calls=1,
511
+ requires=("supported_hypothesis",),
512
+ ))
513
+ registry.register(ToolSpec(
514
+ name="run_fix",
515
+ description="Run the tiered, validated fix module (L1-L4) for the best verified hypothesis.",
516
+ params_schema={"type": "object", "properties": {}},
517
+ handler=_run_fix(loop, state),
518
+ max_calls=1,
519
+ requires=("supported_hypothesis",),
520
+ ))
521
+ registry.register(ToolSpec(
522
+ name="stop",
523
+ description=(
524
+ "End the investigation. params.resolved (bool): true only once a "
525
+ "hypothesis has been tested and is statistically supported and "
526
+ "protocol-consistent — otherwise it is rejected. params.reason (str)."
527
+ ),
528
+ params_schema={
529
+ "type": "object",
530
+ "properties": {"resolved": {"type": "boolean"}, "reason": {"type": "string"}},
531
+ },
532
+ handler=_stop(loop, state),
533
+ # A rejected stop (premature resolved=true, or resolved=false to give up
534
+ # and try again) does no real work, so it shouldn't burn down a scarce
535
+ # quota the way a real M1-M4 stage call does — cap generously.
536
+ max_calls=5,
537
+ ))
538
+ return registry
@@ -0,0 +1,57 @@
1
+ """Checkpoint / heartbeat file I/O for resumable diagnosis runs.
2
+
3
+ Free functions extracted from ``AutoDiagnoseLoop`` (the only current caller,
4
+ in ``legacy.py``) so the atomic-write/read logic is reusable by any future
5
+ loop that wants run_dir-based resume, without depending on the loop class
6
+ itself.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import tempfile
14
+ from datetime import datetime, timezone
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+
19
+ def write_checkpoint(
20
+ checkpoint_path: Path, *, cycle: int, run_id: str, hypothesis_statuses: list[str],
21
+ ) -> None:
22
+ """Atomic checkpoint write (temp-file + rename)."""
23
+ data = {
24
+ "last_completed_cycle": cycle,
25
+ "run_id": run_id,
26
+ "hypothesis_statuses": hypothesis_statuses,
27
+ "timestamp": datetime.now(timezone.utc).isoformat(timespec="seconds"),
28
+ }
29
+ fd, tmp_path = tempfile.mkstemp(
30
+ dir=checkpoint_path.parent, suffix=".tmp", prefix="checkpoint_"
31
+ )
32
+ os.close(fd)
33
+ try:
34
+ Path(tmp_path).write_text(json.dumps(data, indent=2), encoding="utf-8")
35
+ Path(tmp_path).replace(checkpoint_path)
36
+ except BaseException:
37
+ Path(tmp_path).unlink(missing_ok=True)
38
+ raise
39
+
40
+
41
+ def write_heartbeat(heartbeat_path: Path, *, cycle: int, run_id: str) -> None:
42
+ data = {
43
+ "pid": os.getpid(),
44
+ "last_cycle": cycle,
45
+ "run_id": run_id,
46
+ "timestamp": datetime.now(timezone.utc).isoformat(timespec="seconds"),
47
+ }
48
+ heartbeat_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
49
+
50
+
51
+ def read_checkpoint(checkpoint_path: "Path | None") -> "dict[str, Any] | None":
52
+ if checkpoint_path is None or not checkpoint_path.exists():
53
+ return None
54
+ try:
55
+ return json.loads(checkpoint_path.read_text(encoding="utf-8"))
56
+ except (json.JSONDecodeError, OSError):
57
+ return None
@@ -0,0 +1,59 @@
1
+ """Compatibility facade for CLI coding agents and CLI-backed judge models.
2
+
3
+ The implementation lives in ``evalrx.agent_runtime``:
4
+
5
+ - ``evalrx.agent_runtime.providers`` contains CLI coding-provider adapters.
6
+ - ``evalrx.agent_runtime.judges`` contains judge model wrappers.
7
+ - ``evalrx.agent_runtime.cli_types`` contains public config/result types.
8
+
9
+ This module keeps the historical ``evalrx.eval_agent.cli_agent`` import
10
+ path stable.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from evalrx.agent_runtime.cli_types import BINARY_DEFAULTS, CliAgentConfig, CliAgentResult
16
+ from evalrx.agent_runtime.judges.agy import (
17
+ AgyModel,
18
+ )
19
+ from evalrx.agent_runtime.judges.agy import (
20
+ safe_unlink as _safe_unlink,
21
+ )
22
+ from evalrx.agent_runtime.judges.agy import (
23
+ scan_agy_log as _scan_agy_log,
24
+ )
25
+ from evalrx.agent_runtime.judges.claude import ClaudeModel
26
+ from evalrx.agent_runtime.judges.codex import CodexModel
27
+ from evalrx.agent_runtime.providers.antigravity import AntigravityAgent
28
+ from evalrx.agent_runtime.providers.base import CliAgentBase as _CliAgentBase
29
+ from evalrx.agent_runtime.providers.claude_code import ClaudeCodeAgent
30
+ from evalrx.agent_runtime.providers.codex import CodexAgent
31
+ from evalrx.agent_runtime.providers.gemini_cli import GeminiCliAgent
32
+ from evalrx.agent_runtime.providers.kimi_cli import KimiCliAgent
33
+ from evalrx.agent_runtime.providers.opencode import OpenCodeAgent
34
+ from evalrx.agent_runtime.providers.registry import (
35
+ PROVIDER_CLASSES as _PROVIDER_CLASSES,
36
+ )
37
+ from evalrx.agent_runtime.providers.registry import (
38
+ create_cli_agent,
39
+ )
40
+
41
+ __all__ = [
42
+ "AgyModel",
43
+ "AntigravityAgent",
44
+ "BINARY_DEFAULTS",
45
+ "ClaudeCodeAgent",
46
+ "ClaudeModel",
47
+ "CodexModel",
48
+ "CliAgentConfig",
49
+ "CliAgentResult",
50
+ "CodexAgent",
51
+ "GeminiCliAgent",
52
+ "KimiCliAgent",
53
+ "OpenCodeAgent",
54
+ "_CliAgentBase",
55
+ "_PROVIDER_CLASSES",
56
+ "_safe_unlink",
57
+ "_scan_agy_log",
58
+ "create_cli_agent",
59
+ ]
@@ -0,0 +1,5 @@
1
+ """Compatibility facade for CLI Agent Skill installers."""
2
+
3
+ from evalrx.agent_runtime.skills.installer import CodexSkillInstaller, SkillInstaller
4
+
5
+ __all__ = ["CodexSkillInstaller", "SkillInstaller"]