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,68 @@
1
+ """Skill installation strategies for CLI coding-provider adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import shutil
7
+ from pathlib import Path
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class SkillInstaller:
13
+ """Vendor skill directories into a run-local Claude-compatible layout."""
14
+
15
+ def __init__(self, skills: list[str] | tuple[str, ...]) -> None:
16
+ self._skills = list(skills)
17
+
18
+ @property
19
+ def skills(self) -> list[str]:
20
+ return list(self._skills)
21
+
22
+ @property
23
+ def enabled(self) -> bool:
24
+ return bool(self._skills)
25
+
26
+ def install(self, workdir: Path) -> None:
27
+ if not self._skills:
28
+ return
29
+ dest_root = workdir / ".claude" / "skills"
30
+ for src in self._skills:
31
+ src_path = Path(src)
32
+ if not src_path.exists() or not src_path.is_dir():
33
+ logger.warning("skill dir not found, skipping: %s", src)
34
+ continue
35
+ try:
36
+ shutil.copytree(src_path, dest_root / src_path.name, dirs_exist_ok=True)
37
+ except OSError as exc:
38
+ logger.warning("could not vendor skill %s: %s", src, exc)
39
+
40
+
41
+ class CodexSkillInstaller(SkillInstaller):
42
+ """Vendor skills and expose them through AGENTS.md for Codex."""
43
+
44
+ def install(self, workdir: Path) -> None:
45
+ super().install(workdir)
46
+ names = [Path(s).name for s in self._skills if Path(s).is_dir()]
47
+ if not names:
48
+ return
49
+ section = "\n".join([
50
+ "# Agent Skills (vendored)",
51
+ "",
52
+ "Before writing any figure/plot, read and APPLY these style guides:",
53
+ "",
54
+ *[f"- `.claude/skills/{n}/SKILL.md`" for n in names],
55
+ "",
56
+ "They govern chart-type choice and styling only -- never change the "
57
+ "data, the analysis, or the required output format.",
58
+ "",
59
+ ])
60
+ agents_md = workdir / "AGENTS.md"
61
+ try:
62
+ existing = (
63
+ agents_md.read_text(encoding="utf-8").rstrip() + "\n\n"
64
+ if agents_md.exists() else ""
65
+ )
66
+ agents_md.write_text(existing + section, encoding="utf-8")
67
+ except OSError as exc:
68
+ logger.warning("could not write AGENTS.md for codex skills: %s", exc)
@@ -0,0 +1,86 @@
1
+ """Prompt addenda for Agent Skill usage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from evalrx.agent_runtime.cli_types import CliAgentConfig
10
+
11
+
12
+ def fences_hint(cli_config: "CliAgentConfig | None") -> str:
13
+ if cli_config is not None and cli_config.provider != "llm":
14
+ return ", written to a file named analysis.py"
15
+ return " inside a ```python code block"
16
+
17
+
18
+ def skills_hint(cli_config: "CliAgentConfig | None") -> str:
19
+ """Prompt addendum steering the agent to apply available Agent Skills."""
20
+ if cli_config is None or not getattr(cli_config, "skills_enabled", False):
21
+ return ""
22
+
23
+ names = [Path(path).name for path in (cli_config.skills or [])]
24
+ is_codex = getattr(cli_config, "provider", "") == "codex"
25
+
26
+ def _use(nlist: list[str]) -> str:
27
+ if not nlist:
28
+ if is_codex:
29
+ return (
30
+ "read the vendored guides under `.claude/skills/` "
31
+ "(also listed in AGENTS.md) and apply them"
32
+ )
33
+ return "invoke any installed Agent Skills via the Skill tool and follow them"
34
+ if is_codex:
35
+ refs = ", ".join(f"`.claude/skills/{name}/SKILL.md`" for name in nlist)
36
+ return f"read the vendored guides ({refs} -- also listed in AGENTS.md) and apply them"
37
+ listed = ", ".join(f"`/{name}`" for name in nlist)
38
+ return f"invoke the {listed} skill(s) via the Skill tool and follow them"
39
+
40
+ parts: list[str] = []
41
+
42
+ if "outcome-driver-analysis" in names:
43
+ parts.append(
44
+ "ANALYSIS METHOD: BEFORE writing any analysis code, "
45
+ + _use(["outcome-driver-analysis"])
46
+ + " to choose justified statistical methods for the outcome analysis: "
47
+ "explanatory-variable EDA, per-variable tests WITH effect sizes, "
48
+ "conditioning/confounding (Simpson's) checks, marginal screening, a "
49
+ "justified regression model (state the GLM-vs-mixed-effects reasoning "
50
+ "from the ACTUAL clustering structure -- with very few clusters prefer "
51
+ "a fixed effect over a random effect), and fit diagnostics "
52
+ "(collinearity/VIF, discrimination, calibration). Adopt its "
53
+ "METHODOLOGY, not its file layout or report template -- every output "
54
+ "still flows into the required result-JSON contract, and intake "
55
+ "answers are inferred from the data profile and the question (never "
56
+ "stop to ask). Keep all wording DESCRIPTIVE: takeaways carry effect "
57
+ "sizes + confidence intervals + direction; test statistics/p-values "
58
+ "belong in tables/artifacts and must NOT be phrased as significance "
59
+ "or confirmation verdicts -- validity is adjudicated downstream."
60
+ )
61
+
62
+ style = [name for name in names if name != "outcome-driver-analysis"]
63
+ roles = []
64
+ if "eval-chart-style" in style:
65
+ roles.append(
66
+ "`eval-chart-style` governs chart-TYPE choice (distribution-first: "
67
+ "violin/ECDF/heatmap/forest/paired-slope -- never a mean as a bar) and "
68
+ "the palette: FAIL/PASS semantic hues, a categorical series order for "
69
+ "non-outcome dimensions, and single-hue ramps for ordered dimensions "
70
+ "(e.g. model sizes). When any other skill suggests a specific chart "
71
+ "type or color, eval-chart-style's policy and palette take precedence"
72
+ )
73
+ if "nature-figure" in style:
74
+ roles.append("`nature-figure` adds publication-grade matplotlib polish")
75
+ role_line = ("; ".join(roles) + ". ") if roles else ""
76
+ parts.append(
77
+ "FIGURE STYLING: Agent Skills are available -- apply them BY DEFAULT to "
78
+ f"every plot you write under figures/: BEFORE plotting, {_use(style)}. "
79
+ f"{role_line}"
80
+ "This is a non-interactive PYTHON analysis: if a skill asks you to choose "
81
+ "a plotting backend, choose Python and proceed without pausing -- never "
82
+ "stop to ask a question. Use skills for styling only -- they must not "
83
+ "change the data, the analysis, the sandbox workflow, or the final "
84
+ "result JSON."
85
+ )
86
+ return "\n" + "\n\n".join(parts) + "\n"
@@ -0,0 +1,19 @@
1
+ """Resolve bundled and explicitly configured Agent Skills."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from evalrx.agent_assets.skills import SKILL_BACKENDS, bundled_skill_paths
6
+
7
+
8
+ def resolve_skill_paths(
9
+ *,
10
+ provider: str,
11
+ explicit: list[str] | tuple[str, ...] = (),
12
+ use_bundled: bool = True,
13
+ ) -> tuple[str, ...]:
14
+ """Return ordered skill dirs for a provider, de-duplicating explicit repeats."""
15
+ skill_dirs = list(explicit or [])
16
+ if use_bundled and provider in SKILL_BACKENDS:
17
+ bundled = [path for path in bundled_skill_paths() if path not in skill_dirs]
18
+ skill_dirs = bundled + skill_dirs
19
+ return tuple(skill_dirs)
@@ -0,0 +1,132 @@
1
+ """Public data-analysis API: exploratory analysis, hypothesis proposal, and
2
+ statistical confirmation.
3
+
4
+ This package exposes EvalRX' data-analysis layer as a standalone
5
+ capability — ``ExploratoryAnalysisAgent`` (M2) for descriptive EDA (no
6
+ hypothesis generation or validation itself), ``HypothesisAgent`` (M3) for
7
+ proposing falsifiable hypotheses from M2's takeaways (proposal only, no
8
+ validation), and ``StatsAnalysisAgent`` for confirmatory effect/CI/e-value/FDR
9
+ verdicts. The eval-agent loops still use the same implementation internally,
10
+ but callers do not need to import from ``evalrx.eval_agent.stages``.
11
+ """
12
+
13
+ from evalrx.analysis.adjudicate import adjudicate_report, adjudicate_signals
14
+ from evalrx.analysis.api import ExploreRunResult, explore
15
+ from evalrx.analysis.explorer import (
16
+ CandidateSignal,
17
+ ExploratoryAnalysisAgent,
18
+ ExploratoryAnalysisReport,
19
+ Takeaway,
20
+ load_records_from_path,
21
+ scan_folder,
22
+ )
23
+ from evalrx.analysis.failure_modes import FailureMode, FailureModeReport, cluster_failures
24
+ from evalrx.analysis.fused_pipeline import (
25
+ FusedReport,
26
+ FusedSignal,
27
+ run_fused_analysis,
28
+ )
29
+ from evalrx.analysis.hypothesis_agent import Hypothesis, HypothesisAgent
30
+ from evalrx.analysis.operationalize import (
31
+ RecipeError,
32
+ SignalRecipe,
33
+ bridge_recipes_to_result,
34
+ compile_recipe,
35
+ compile_recipes,
36
+ per_case_finding,
37
+ per_case_to_records,
38
+ safe_ident,
39
+ )
40
+ from evalrx.analysis.planner import AnalysisPlanItem, plan_stats_input, ranked_signal_names
41
+ from evalrx.analysis.profile import (
42
+ ColumnProfile,
43
+ DatasetProfile,
44
+ describe_outcome,
45
+ profile_records,
46
+ profile_stats_input,
47
+ )
48
+ from evalrx.analysis.run_codebase import CodebaseRunResult, run_codebase, run_codebase_cli
49
+ from evalrx.analysis.stats_agent import StatsAnalysisAgent, StatsAnalysisReport
50
+ from evalrx.analysis.stats_tools import (
51
+ STATS_TOOL_CATALOG,
52
+ EvidenceResult,
53
+ StatsInput,
54
+ StatsToolResult,
55
+ build_stats_input,
56
+ build_stats_input_from_records,
57
+ default_plan,
58
+ fdr_correct,
59
+ run_stats_tool,
60
+ )
61
+ from evalrx.analysis.workbench import (
62
+ DatasetBundle,
63
+ EventSink,
64
+ ThreadStore,
65
+ UploadLimits,
66
+ extract_archive,
67
+ ingest_directory,
68
+ )
69
+ from evalrx.reporting.compiler import compile_diagnostic_report
70
+ from evalrx.reporting.model import Claim, DiagnosticReport, Evidence, ReportStep
71
+
72
+ __all__ = [
73
+ "explore",
74
+ "ExploreRunResult",
75
+ "run_codebase",
76
+ "run_codebase_cli",
77
+ "CodebaseRunResult",
78
+ "cluster_failures",
79
+ "FailureMode",
80
+ "FailureModeReport",
81
+ "StatsAnalysisAgent",
82
+ "StatsAnalysisReport",
83
+ "ExploratoryAnalysisAgent",
84
+ "ExploratoryAnalysisReport",
85
+ "Takeaway",
86
+ "CandidateSignal",
87
+ "Hypothesis",
88
+ "HypothesisAgent",
89
+ "adjudicate_report",
90
+ "adjudicate_signals",
91
+ "SignalRecipe",
92
+ "compile_recipe",
93
+ "compile_recipes",
94
+ "per_case_finding",
95
+ "per_case_to_records",
96
+ "bridge_recipes_to_result",
97
+ "safe_ident",
98
+ "RecipeError",
99
+ "ColumnProfile",
100
+ "DatasetProfile",
101
+ "describe_outcome",
102
+ "profile_records",
103
+ "profile_stats_input",
104
+ "AnalysisPlanItem",
105
+ "ranked_signal_names",
106
+ "plan_stats_input",
107
+ "run_fused_analysis",
108
+ "FusedReport",
109
+ "FusedSignal",
110
+ "DiagnosticReport",
111
+ "Claim",
112
+ "Evidence",
113
+ "ReportStep",
114
+ "compile_diagnostic_report",
115
+ "load_records_from_path",
116
+ "scan_folder",
117
+ "StatsInput",
118
+ "StatsToolResult",
119
+ "EvidenceResult",
120
+ "STATS_TOOL_CATALOG",
121
+ "build_stats_input",
122
+ "build_stats_input_from_records",
123
+ "default_plan",
124
+ "fdr_correct",
125
+ "run_stats_tool",
126
+ "DatasetBundle",
127
+ "EventSink",
128
+ "ThreadStore",
129
+ "UploadLimits",
130
+ "extract_archive",
131
+ "ingest_directory",
132
+ ]
@@ -0,0 +1,154 @@
1
+ """Host-side adjudication of explorer candidate signals (the firewall).
2
+
3
+ The :mod:`~evalrx.analysis.explorer` PROPOSES candidate signals; it has no
4
+ authority to declare significance. This module is the host firewall: it takes the
5
+ host-adjudicable ``sufficient`` statistics an explorer attached to a candidate and
6
+ recomputes the verdict with the SAME validated, multiplicity-aware core the M2
7
+ stats engine uses — never trusting a ``reject`` / ``e_value`` / ``p_value`` the
8
+ explorer self-declared (the explorer schema does not even carry one).
9
+
10
+ Mechanics (mirror ``stats_tools`` / ``stats_tool_generator`` exactly):
11
+
12
+ - ``_reconstruct_decision`` turns sufficient statistics into
13
+ ``(reject, e_value, effect, ci)`` via the validated core.
14
+ - ``fdr_correct`` applies e-BH across every candidate that produced an e-value
15
+ (the ``paired_binary`` shape), so proposing more candidates pays more
16
+ multiplicity — exactly one e-BH family, no per-candidate self-rejection.
17
+ - A ``two_group`` candidate yields a bootstrap-CI reject but no e-value (matching
18
+ the ``signal_label_assoc`` catalog tool). It is host-adjudicated but sits
19
+ OUTSIDE the e-BH family and is flagged ``fdr_corrected=False`` — honest, not
20
+ over-claimed.
21
+ - A candidate with no adjudicable ``sufficient`` is ``descriptive_only`` and never
22
+ rejects.
23
+
24
+ IMPORTANT — split discipline: in the standalone chat path the candidate's
25
+ ``sufficient`` is computed on the SAME rows used to discover the signal. That is
26
+ IN-SAMPLE; ``split_label`` records it so nothing over-claims, and an honest caveat
27
+ is appended. Held-out CONFIRM-split adjudication (selecting on EXPLORE, computing
28
+ each e-value on a disjoint CONFIRM split) is the job of the fused pipeline
29
+ (Phase B). This module supplies only the verdict mechanism.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ from typing import TYPE_CHECKING, Any
35
+
36
+ from evalrx.analysis.stats_tool_generator import _reconstruct_decision
37
+ from evalrx.analysis.stats_tools import StatsToolResult, fdr_correct
38
+
39
+ if TYPE_CHECKING:
40
+ from evalrx.analysis.explorer import CandidateSignal, ExploratoryAnalysisReport
41
+
42
+
43
+ def _family_key(candidate: "CandidateSignal", index: int) -> str:
44
+ """Stable, unique key so an e-BH rejection maps back to the right candidate
45
+ even when two candidates share a name."""
46
+ return f"{candidate.name or 'signal'}#{index}"
47
+
48
+
49
+ def adjudicate_signals(
50
+ candidates: "list[CandidateSignal]",
51
+ *,
52
+ alpha: float = 0.05,
53
+ split_label: str = "in_sample",
54
+ ) -> dict[str, Any]:
55
+ """Annotate each candidate IN PLACE with a host verdict; return family metadata.
56
+
57
+ Reads only ``candidate.sufficient`` — never a self-declared verdict. Returns a
58
+ metadata dict describing the single e-BH family the candidates competed in.
59
+ """
60
+ # 1) Host reconstruction per candidate; assemble the e-BH family from the
61
+ # candidates that produced an e-value (paired_binary).
62
+ raw_reject: dict[int, bool] = {}
63
+ family: list[StatsToolResult] = []
64
+ family_key: dict[int, str] = {}
65
+ for i, c in enumerate(candidates):
66
+ recon = (
67
+ _reconstruct_decision(c.sufficient, alpha)
68
+ if isinstance(c.sufficient, dict)
69
+ else None
70
+ )
71
+ if recon is None:
72
+ c.host_adjudicated = False
73
+ c.descriptive_only = True
74
+ c.fdr_corrected = False
75
+ c.e_value = None
76
+ c.reject = False
77
+ continue
78
+
79
+ reject, e_value, effect, ci = recon
80
+ c.host_adjudicated = True
81
+ c.descriptive_only = False
82
+ c.e_value = e_value
83
+ if effect is not None:
84
+ c.effect = effect
85
+ if ci is not None:
86
+ c.ci = ci
87
+ raw_reject[i] = bool(reject)
88
+ if e_value is not None:
89
+ key = _family_key(c, i)
90
+ family_key[i] = key
91
+ family.append(
92
+ StatsToolResult(
93
+ tool=key, e_value=e_value, effect=effect, ci=ci, reject=bool(reject)
94
+ )
95
+ )
96
+
97
+ # 2) ONE e-BH family across the e-value-bearing candidates (the multiplicity
98
+ # firewall). `fdr_correct` only considers results with an e-value.
99
+ fdr = fdr_correct(family, alpha=alpha)
100
+ rejected_keys = set(fdr.get("rejected_tools", []))
101
+
102
+ # 3) Final reject per candidate.
103
+ for i, c in enumerate(candidates):
104
+ if not c.host_adjudicated:
105
+ continue
106
+ if c.e_value is not None:
107
+ # In the e-BH family. `reject` IS the e-BH outcome; `fdr_corrected`
108
+ # means "this rejection is FDR-controlled" — True ONLY when it survived
109
+ # e-BH (matches fused_pipeline; a family member that did not survive is
110
+ # reject=False, fdr_corrected=False). Family membership is recoverable
111
+ # from `e_value is not None`, so no information is lost.
112
+ c.reject = family_key.get(i) in rejected_keys
113
+ c.fdr_corrected = bool(c.reject)
114
+ else:
115
+ # two_group: CI-based reject, outside the e-BH family (mirrors M2's
116
+ # signal_label_assoc). Honest about not being FDR-corrected.
117
+ c.fdr_corrected = False
118
+ c.reject = raw_reject.get(i, False)
119
+
120
+ return {
121
+ "method": "e-BH",
122
+ "alpha": alpha,
123
+ "split": split_label,
124
+ "n_candidates": len(candidates),
125
+ "n_host_adjudicated": sum(1 for c in candidates if c.host_adjudicated),
126
+ "n_in_family": len(family), # e-value-bearing candidates under e-BH
127
+ "n_rejected": sum(1 for c in candidates if c.reject),
128
+ "rejected": [c.name for c in candidates if c.reject],
129
+ }
130
+
131
+
132
+ def adjudicate_report(
133
+ report: "ExploratoryAnalysisReport",
134
+ *,
135
+ alpha: float = 0.05,
136
+ split_label: str = "in_sample",
137
+ ) -> "ExploratoryAnalysisReport":
138
+ """Run :func:`adjudicate_signals` over a report's candidates, record metadata.
139
+
140
+ Mutates and returns *report*. Appends an honest caveat when any verdict is
141
+ in-sample (no held-out CONFIRM split was used to compute the e-values).
142
+ """
143
+ meta = adjudicate_signals(
144
+ report.candidate_signals, alpha=alpha, split_label=split_label
145
+ )
146
+ report.adjudication = meta
147
+ if meta["n_host_adjudicated"] and split_label == "in_sample":
148
+ caveat = (
149
+ "host verdicts are IN-SAMPLE (computed on the rows used to discover "
150
+ "the signal); confirm on a held-out split before trusting"
151
+ )
152
+ if caveat not in report.caveats:
153
+ report.caveats.append(caveat)
154
+ return report