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
evalrx/contract/m5.py ADDED
@@ -0,0 +1,292 @@
1
+ """M5 — intervention and repair. Two different jobs under one number.
2
+
3
+ ``SurgeryAgent`` INTERVENES: it changes one variable, re-runs, and reads the
4
+ result as causal evidence about *why*. Its output is a verdict, not a deliverable
5
+ -- the modified inputs are discarded.
6
+
7
+ ``FixAgent`` REPAIRS: it proposes changes intended to survive, and validates them
8
+ paired against the unmodified baseline on the same cases.
9
+
10
+ Conflating them produces the pipeline's worst failure mode: presenting a
11
+ diagnostic manipulation as a shippable fix.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any, Literal
17
+
18
+ from pydantic import Field, model_validator
19
+
20
+ from evalrx.contract.common import (
21
+ CaseBatchRef,
22
+ HypothesisStatus,
23
+ StageEnvelope,
24
+ WireModel,
25
+ )
26
+ from evalrx.contract.methodology import MethodologyWire
27
+
28
+ # ===========================================================================
29
+ # M5a — SurgeryAgent: verify the mechanism
30
+ # ===========================================================================
31
+
32
+ class SurgeryInput(WireModel):
33
+ """One hypothesis at a time. Surgery is per-claim by construction."""
34
+
35
+ hypothesis_id: str
36
+ results_ref: str = Field(description="Path to the M1 ProbeOutput, for per-case signal extraction.")
37
+ data: CaseBatchRef
38
+ model_name: str
39
+
40
+
41
+ class InterventionOutput(StageEnvelope):
42
+ """``SurgeryAgent.operate`` result — causal evidence, not a repair.
43
+
44
+ ``fixed`` here means "the intervention completely separated failing from
45
+ passing cases", i.e. the mechanism is confirmed. It does NOT mean a usable
46
+ repair exists; that claim belongs to :class:`FixOutput` alone.
47
+ """
48
+
49
+ hypothesis_id: str = Field(min_length=1, description="Points into the M3 DiagnosisOutput.")
50
+ hypothesis_status: HypothesisStatus = Field(
51
+ description="Verdict on the hypothesis. Named apart from the envelope's `status`, "
52
+ "which is this STAGE's execution state — they answer different questions."
53
+ )
54
+ strategy: Literal["verify_fn", "analyzer_params", "experiment_writer", "passive_correlation"] = (
55
+ "passive_correlation"
56
+ )
57
+ fixed: bool = Field(
58
+ default=False, description="Mechanism fully separates the groups. NOT a shippable repair."
59
+ )
60
+ confidence_score: float = Field(default=0.0, ge=0.0, le=1.0)
61
+ evidence_dimensions: dict[str, float] = Field(
62
+ default_factory=dict, description="Breakdown: evidence gap / sample adequacy / control cleanliness."
63
+ )
64
+ evidence: dict[str, Any] = Field(default_factory=dict)
65
+ methodology: MethodologyWire | None = Field(
66
+ default=None,
67
+ description="The manipulation performed: which variable was changed, what was held fixed, "
68
+ "what was compared. This is what makes the causal claim auditable.",
69
+ )
70
+ trial_root: str | None = Field(
71
+ default=None,
72
+ description="Attempt folder when the experiment-writer strategy ran: generated code, "
73
+ "stdout/stderr, workspace and record all sit there.",
74
+ )
75
+ new_data: CaseBatchRef | None = Field(
76
+ default=None,
77
+ description="When SUPPORTED: the cases NOT in the signal group — the refined subset for the "
78
+ "next M1 cycle. null means no refinement, which differs from an empty batch.",
79
+ )
80
+
81
+ @property
82
+ def is_intervention(self) -> bool:
83
+ """Whether this run can ground a causal claim.
84
+
85
+ ``passive_correlation`` only observes, so it cannot — which makes this a
86
+ function of ``strategy``, not an independent fact worth storing.
87
+ """
88
+ return self.strategy != "passive_correlation"
89
+
90
+
91
+ # ===========================================================================
92
+ # M5b — FixAgent: propose and validate repairs
93
+ # ===========================================================================
94
+
95
+ class FixInput(WireModel):
96
+ """Repairs are attempted only on hypotheses M4 verified. ``max_tier`` bounds
97
+ how invasive the search may get and is the caller's decision, never the
98
+ agent's."""
99
+
100
+ hypothesis_ids: list[str]
101
+ data: CaseBatchRef
102
+ model_name: str
103
+ max_tier: Literal["L0", "L1", "L2", "L3a", "L3b", "L4"] = Field(
104
+ description="L0 runtime config / L1 prompt / L2 scaffold / L3a internals-read / "
105
+ "L3b internals-write / L4 params.\n\n"
106
+ "L0 is in this list because `FixTier` has it — decoding settings are a "
107
+ "real, and the least invasive, place to intervene. Omitting it made a "
108
+ "tier the pipeline can produce unrepresentable."
109
+ )
110
+ max_repair_rounds: int = Field(
111
+ default=1, ge=1, description="Feedback-driven propose->validate rounds. 1 = no loop."
112
+ )
113
+ prior_attempts_ref: str | None = None
114
+
115
+
116
+ class FixAttemptWire(WireModel):
117
+ """One candidate validated against the unmodified baseline on the SAME cases.
118
+
119
+ Every count here is a PAIRED flip, never a rate difference. "repaired 18 /
120
+ broke 1" is a statement about individual cases changing sign; a pass-rate
121
+ delta is not the same claim and must never be displayed as if it were.
122
+ """
123
+
124
+ tier: Literal["L0", "L1", "L2", "L3a", "L3b", "L4"]
125
+
126
+ ref: str = Field(
127
+ default="",
128
+ description="The reader-facing identifier, e.g. `R3`. Assigned by the producer over the "
129
+ "whole run and STABLE across `selection` and `attempted`, so a reader who "
130
+ "sees R3 in the sweep can find R3's card and know it is the same repair.\n\n"
131
+ "This exists because `name` cannot do the job. Consumers that numbered rows "
132
+ "by list position gave the same repair two different numbers in the two "
133
+ "lists, which is worse than no number at all. Empty means the producer "
134
+ "assigned none; a consumer may then number by position, but only within one "
135
+ "list and never as a cross-list identity.",
136
+ )
137
+ name: str = Field(
138
+ description="Machine slug, e.g. `audio_evidence_then_answer`. It is the JOIN KEY — "
139
+ "`best`, `selected_on_explore` and `ebh_survivors` all name a candidate this "
140
+ "way — and it is what the attempt folder on disk is called.\n\n"
141
+ "It is NOT the label to show a reader. These slugs are chosen by the agent "
142
+ "while naming its log files, so they read as identifiers rather than as "
143
+ "language. Show `ref` to point at a candidate and `headline` to say what it "
144
+ "does.",
145
+ )
146
+ headline: str = Field(
147
+ default="",
148
+ description="One plain sentence saying what this repair changes, written for a reader "
149
+ "with no background in the field: no tier names, no metric names, no jargon, "
150
+ "no slug echoed back. \"Ask the model to describe what it hears before "
151
+ "answering\" — not \"audio-evidence-first prompt scaffold\".\n\n"
152
+ "Empty when the producer had nothing readable to offer, which a consumer "
153
+ "should render as an honest blank rather than by humanising the slug: "
154
+ "`audio_evidence_then_answer` -> \"Audio Evidence Then Answer\" invents "
155
+ "fluency the run never had.",
156
+ )
157
+ kind: str | None = None
158
+ source: str | None = None
159
+ methodology: MethodologyWire | None = Field(
160
+ default=None,
161
+ description="What this candidate DOES, as a graph plus a rendered draw.io diagram. The "
162
+ "payload is the implementation; this is the explanation. Required for any "
163
+ "candidate a reader is asked to accept — a repair nobody can follow cannot "
164
+ "be reviewed, only trusted.",
165
+ )
166
+ trial_root: str | None = Field(
167
+ default=None,
168
+ description="Self-contained attempt folder, RELATIVE TO THE RUN ROOT. Preferred join "
169
+ "key across rounds — name alone is not unique.\n\n"
170
+ "It holds `outputs.jsonl`, one line per validated case: "
171
+ "`{case_id, status, output}` where status is fixed / broken / unchanged. "
172
+ "That is what a reader needs to study a repair case by case — the "
173
+ "candidate's actual answer beside the baseline's — and it is kept out of "
174
+ "this payload because it is one row per case per candidate. Recorded "
175
+ "relative because an absolute producer path resolves nowhere else, which "
176
+ "is the same reason MediaRef paths are relative.",
177
+ )
178
+
179
+ n_pairs: int = 0
180
+ n_baseline_correct: int = 0
181
+ n_fixed: int = 0
182
+ n_broken: int = 0
183
+ fixed_cases: list[str] = Field(default_factory=list)
184
+ broken_cases: list[str] = Field(default_factory=list)
185
+
186
+ n_applicable: int = Field(default=0, description="Cases the candidate actually touched.")
187
+ coverage: float | None = Field(default=None, description="applicable FAILs / total FAILs.")
188
+ n_unstable: int = Field(
189
+ default=0, description="Dropped as baseline-unstable, so sampling noise is not read as regression."
190
+ )
191
+ n_model_independent: int = Field(
192
+ default=0,
193
+ description="Cases the scaffold solved by its own computation under frozen_model_control — "
194
+ "excluded from the paired test rather than counted as fixed.",
195
+ )
196
+
197
+ effect: float | None = None
198
+ e_value: float | None = None
199
+ reject: bool = Field(default=False, description="This candidate's own gate. Not sufficient to win.")
200
+ verdict: Literal[
201
+ "fixed", "partial", "unsafe", "regressed", "no_effect", "not_executed", "model_independent"
202
+ ] = Field(
203
+ description="The candidate's outcome. Subsumes a separate `fixed` boolean "
204
+ "(verdict == 'fixed') and a separate exec_error ('not_executed')."
205
+ )
206
+ summary: str = ""
207
+
208
+
209
+ class FixOutput(StageEnvelope):
210
+ """``FixAgent.propose_and_validate`` result.
211
+
212
+ What each candidate literally applied — the prompt text, the generated code —
213
+ is not here. It is the ``result.json`` under ``trial_root``, which is also
214
+ where the per-case outputs and the execution record sit; inlining a code blob
215
+ would put a second, drifting copy in every report a reader loads.
216
+ """
217
+
218
+ max_tier: Literal["L0", "L1", "L2", "L3a", "L3b", "L4"]
219
+ routed: list[dict[str, str]] = Field(
220
+ default_factory=list, description="Which hypothesis went to which tier, and why."
221
+ )
222
+
223
+ attempted: list[FixAttemptWire] = Field(
224
+ default_factory=list,
225
+ description="The CONFIRMATION evidence: candidates validated on the held-out split. "
226
+ "With a confirm split in play this is exactly one frozen candidate — "
227
+ "selecting and testing on the same cases is the error the two-stage "
228
+ "protocol exists to prevent.",
229
+ )
230
+ selection: list[FixAttemptWire] = Field(
231
+ default_factory=list,
232
+ description="The candidates tried while CHOOSING one, on the diagnosis split. "
233
+ "Descriptive only — never confirmation evidence, and a reader must not "
234
+ "add these effect sizes to the ones above.\n\n"
235
+ "Carried because omitting it misrepresents the search: a run that tried "
236
+ "seven candidates across L1 and L2 and confirmed one L1 reported a "
237
+ "single L1 row, and every reader concluded L2 was never attempted. "
238
+ "What was ruled out is part of what the run found — an `unsafe` "
239
+ "candidate that repaired 7 cases and broke 8 is a result.",
240
+ )
241
+ selected_on_explore: str | None = Field(
242
+ default=None,
243
+ description="Which candidate the selection phase froze for confirmation. Names a "
244
+ "row in `selection`; `best` names one in `attempted`.",
245
+ )
246
+
247
+ best: str | None = Field(
248
+ default=None, description="Winning candidate NAME. The row itself is in `attempted`."
249
+ )
250
+ fixed: bool = False
251
+ ebh_survivors: list[str] = Field(
252
+ default_factory=list,
253
+ description="Candidate names surviving e-BH across the whole tested family. A winner must be "
254
+ "BOTH individually `fixed` AND in this set — best-of-N needs its own correction.",
255
+ )
256
+ repair_rounds: int = Field(default=0, ge=0)
257
+ recommendation: dict[str, Any] | None = Field(
258
+ default=None,
259
+ description="{recommend_tier, reason} when nothing validated. Escalation is NEVER automatic.",
260
+ )
261
+ refine_signal: dict[str, Any] | None = Field(
262
+ default=None,
263
+ description="Set when a candidate helped one subset and hurt another — evidence the hypothesis "
264
+ "should be re-scoped, not that no fix exists. Feedback edge back into M3.",
265
+ )
266
+
267
+ @model_validator(mode="after")
268
+ def _fixed_requires_confirmed_survivor(self) -> "FixOutput":
269
+ if not self.fixed:
270
+ return self
271
+ if self.best is None:
272
+ raise ValueError("fixed=True requires a `best` candidate")
273
+ row = next((a for a in self.attempted if a.name == self.best), None)
274
+ if row is None:
275
+ raise ValueError(f"best={self.best!r} is not among the validated attempts")
276
+ if row.methodology is None:
277
+ raise ValueError(
278
+ "fixed=True without a methodology on the winning candidate: a shipped repair "
279
+ "must carry an explanation a reviewer can read"
280
+ )
281
+ if self.ebh_survivors and self.best not in self.ebh_survivors:
282
+ raise ValueError(
283
+ f"best={self.best!r} is not an e-BH survivor; best-of-N selection without "
284
+ "family correction is not a validated fix"
285
+ )
286
+ return self
287
+
288
+
289
+ __all__ = [
290
+ "SurgeryInput", "InterventionOutput",
291
+ "FixInput", "FixAttemptWire", "FixOutput",
292
+ ]
@@ -0,0 +1,76 @@
1
+ """Methodology — what an M5 repair or intervention actually DOES.
2
+
3
+ A candidate's method currently survives only as a code blob or a prompt string.
4
+ Neither answers the question a reader has ("what does this do to my model?"), so
5
+ the contract carries a diagram instead.
6
+
7
+ The contract is the draw.io XML and nothing else: a frontend embeds
8
+ ``drawio_xml`` directly, with no toolchain and no intermediate format to
9
+ interpret. How a producer arrives at that XML is its own business —
10
+ ``examples/contract/drawio_builder.py`` offers a step-graph with automatic
11
+ layout for producers that want one.
12
+
13
+ The XML is still validated on its own terms. A diagram whose edge points at a
14
+ cell that does not exist renders as a perfectly ordinary picture, so nothing
15
+ downstream — and no reader — would ever notice; that check has to happen here or
16
+ not at all.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import xml.etree.ElementTree as ET
22
+
23
+ from pydantic import Field, model_validator
24
+
25
+ from evalrx.contract.common import WireModel
26
+
27
+
28
+ class MethodologyWire(WireModel):
29
+ """A repair or intervention, as an embeddable diagram plus plain language."""
30
+
31
+ title: str = Field(min_length=1, max_length=120)
32
+ summary: str = Field(
33
+ default="", max_length=1000,
34
+ description="Two or three sentences a non-specialist can follow. Not the code.",
35
+ )
36
+ tier: str = Field(default="", description="L1..L4 for a fix; empty for an intervention.")
37
+ drawio_xml: str = Field(
38
+ min_length=1,
39
+ description="Uncompressed mxfile document, embeddable as-is. Compressed diagrams are "
40
+ "rejected: they cannot be diffed, reviewed, or repaired by hand.",
41
+ )
42
+ n_model_calls: int | None = Field(
43
+ default=None,
44
+ description="Model calls this method makes per case. A large claimed gain alongside few "
45
+ "model calls is the shape of a model-independent result, where the scaffold "
46
+ "rather than the model did the work.",
47
+ )
48
+
49
+ @model_validator(mode="after")
50
+ def _xml_is_renderable(self) -> "MethodologyWire":
51
+ try:
52
+ root = ET.fromstring(self.drawio_xml)
53
+ except ET.ParseError as exc:
54
+ raise ValueError(f"drawio_xml is not well-formed XML: {exc}") from exc
55
+ if root.tag != "mxfile":
56
+ raise ValueError(f"drawio_xml root must be <mxfile>, got <{root.tag}>")
57
+ cells = list(root.iter("mxCell"))
58
+ if not cells:
59
+ raise ValueError("drawio_xml contains no mxCell elements")
60
+ ids = [c.get("id") for c in cells if c.get("id")]
61
+ dupes = {i for i in ids if ids.count(i) > 1}
62
+ if dupes:
63
+ raise ValueError(f"duplicate mxCell ids render unpredictably: {sorted(dupes)}")
64
+ known = set(ids)
65
+ for c in cells:
66
+ for end in ("source", "target"):
67
+ ref = c.get(end)
68
+ if ref and ref not in known:
69
+ raise ValueError(
70
+ f"edge {c.get('id')!r} has {end}={ref!r}, which is not a cell in this "
71
+ "diagram — it draws as an arrow into empty space"
72
+ )
73
+ return self
74
+
75
+
76
+ __all__ = ["MethodologyWire"]
@@ -0,0 +1,58 @@
1
+ """Pre-M1 — probe search: synthesize new test cases instead of analyzing old ones.
2
+
3
+ Optional and structurally different from every other stage: its output is DATA,
4
+ not a measurement or a verdict, so it is the one stage whose result can be fed
5
+ back in as another stage's input.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pydantic import Field, model_validator
11
+
12
+ from evalrx.contract.common import CaseBatchRef, StageEnvelope, WireModel
13
+ from evalrx.contract.m1 import ModelRef, ProtocolWire
14
+
15
+
16
+ class ProbeSearchInput(WireModel):
17
+ """Seeds plus a budget. The search anchors both regime trees on the seeds."""
18
+
19
+ model: ModelRef
20
+ seed_pool: CaseBatchRef
21
+ protocol: ProtocolWire | None = None
22
+ budget: int = Field(default=20, ge=1, description="Total simulations (T_max).")
23
+ beta: float = Field(default=1.0, description="UCB exploration constant.")
24
+ w_max: int = Field(default=3, ge=1, description="Max children before progressive widening.")
25
+
26
+
27
+ class ProbeSearchOutput(StageEnvelope):
28
+ """Discovered cases plus search accounting.
29
+
30
+ ``all_cases`` — not ``failure_cases`` — is what M1 should normally receive:
31
+ a batch of nothing but FAILs has no control group, and every downstream
32
+ comparison degenerates.
33
+
34
+ The in-memory result also holds ``macro_root`` / ``micro_root``, two trees
35
+ whose nodes point at both parent and children. That graph is cyclic and
36
+ cannot be serialized naively, which is why the search trajectory is carried
37
+ here as an explicit flat edge list rather than a nested object.
38
+ """
39
+
40
+ n_simulations: int = Field(ge=0)
41
+ n_macro: int = Field(ge=0)
42
+ n_micro: int = Field(ge=0)
43
+
44
+ all_cases: CaseBatchRef = Field(description="Every evaluated case, PASS and FAIL. Feed this to M1.")
45
+ failure_cases: CaseBatchRef = Field(description="The FAIL subset. Feed this to cluster_failures.")
46
+
47
+ @property
48
+ def error_rate(self) -> float:
49
+ return self.failure_cases.n_cases / self.n_simulations if self.n_simulations else 0.0
50
+
51
+ @model_validator(mode="after")
52
+ def _counts_are_consistent(self) -> "ProbeSearchOutput":
53
+ if self.failure_cases.n_cases > self.all_cases.n_cases:
54
+ raise ValueError("failure_cases cannot exceed all_cases")
55
+ return self
56
+
57
+
58
+ __all__ = ["ProbeSearchInput", "ProbeSearchOutput"]
@@ -0,0 +1,140 @@
1
+ """JSON Schema -> TypeScript for the stage contract.
2
+
3
+ Written in Python on purpose. ``export.py`` documented ``npx
4
+ json-schema-to-typescript`` as the way to get types, which puts a node toolchain
5
+ and a network fetch between a Python field and the frontend that has to decode
6
+ it — and a generation step that can fail for reasons unrelated to the contract
7
+ is a generation step CI will end up skipping. One language, one command, one
8
+ committed artifact that a diff check can police.
9
+
10
+ The output is a single ``.d.ts``: every stage's payload and every type it
11
+ references, so a frontend imports one file and gets the whole pipeline.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any
17
+
18
+ _SCALARS = {
19
+ "string": "string", "integer": "number", "number": "number",
20
+ "boolean": "boolean", "null": "null",
21
+ }
22
+
23
+
24
+ def _ts_type(schema: dict[str, Any]) -> str:
25
+ """One JSON-Schema node as a TypeScript type expression."""
26
+ if not schema:
27
+ return "unknown"
28
+ if "$ref" in schema:
29
+ return schema["$ref"].rsplit("/", 1)[-1]
30
+ if "const" in schema:
31
+ return _literal(schema["const"])
32
+ if "enum" in schema:
33
+ return " | ".join(_literal(v) for v in schema["enum"]) or "never"
34
+ for key in ("anyOf", "oneOf"):
35
+ if key in schema:
36
+ parts = [_ts_type(s) for s in schema[key]]
37
+ # dict.fromkeys, not set: a stable union order keeps the generated
38
+ # file diffable, and a diff check is the only thing that makes a
39
+ # generated artifact trustworthy.
40
+ return " | ".join(dict.fromkeys(parts)) or "unknown"
41
+ if "allOf" in schema and len(schema["allOf"]) == 1:
42
+ return _ts_type(schema["allOf"][0])
43
+
44
+ kind = schema.get("type")
45
+ if isinstance(kind, list):
46
+ return " | ".join(dict.fromkeys(_SCALARS.get(k, "unknown") for k in kind))
47
+ if kind == "array":
48
+ items = schema.get("items") or schema.get("prefixItems")
49
+ if isinstance(items, list): # tuple, e.g. ci: (float, float)
50
+ return "[" + ", ".join(_ts_type(i) for i in items) + "]"
51
+ return f"({_ts_type(items or {})})[]"
52
+ if kind == "object":
53
+ extra = schema.get("additionalProperties")
54
+ value = _ts_type(extra) if isinstance(extra, dict) else "unknown"
55
+ return f"{{ [key: string]: {value} }}"
56
+ return _SCALARS.get(kind, "unknown")
57
+
58
+
59
+ def _literal(value: Any) -> str:
60
+ if isinstance(value, str):
61
+ return '"' + value.replace('"', '\\"') + '"'
62
+ if isinstance(value, bool):
63
+ return "true" if value else "false"
64
+ if value is None:
65
+ return "null"
66
+ return str(value)
67
+
68
+
69
+ def _doc(schema: dict[str, Any], indent: str) -> str:
70
+ """Carry the Python docstring/description across. A generated type with no
71
+ prose sends the reader back to the Python file, which is the drift this
72
+ whole contract exists to avoid."""
73
+ text = (schema.get("description") or "").strip()
74
+ if not text:
75
+ return ""
76
+ lines = [ln.rstrip() for ln in text.splitlines()]
77
+ body = "\n".join(f"{indent} * {ln}".rstrip() for ln in lines)
78
+ return f"{indent}/**\n{body}\n{indent} */\n"
79
+
80
+
81
+ def _interface(name: str, schema: dict[str, Any]) -> str:
82
+ if "enum" in schema and "properties" not in schema:
83
+ return f"{_doc(schema, '')}export type {name} = {_ts_type(schema)};\n"
84
+ props = schema.get("properties") or {}
85
+ required = set(schema.get("required") or ())
86
+ out = [_doc(schema, ""), f"export interface {name} {{\n"]
87
+ for field, sub in props.items():
88
+ opt = "" if field in required else "?"
89
+ out.append(_doc(sub, " "))
90
+ out.append(f" {field}{opt}: {_ts_type(sub)};\n")
91
+ if schema.get("additionalProperties") is True:
92
+ out.append(
93
+ " /** Producer-defined keys. The envelope is closed; payload areas are not. */\n"
94
+ " [key: string]: unknown;\n"
95
+ )
96
+ out.append("}\n")
97
+ return "".join(out)
98
+
99
+
100
+ def render(
101
+ schemas: dict[str, dict[str, Any]], *, schema_version: int,
102
+ aliases: "dict[str, tuple[Any, ...]] | None" = None,
103
+ ) -> str:
104
+ """Render every stage schema plus its ``$defs`` into one declaration file.
105
+
106
+ ``aliases`` names literal unions that pydantic inlines at every use site
107
+ (``Modality`` is one). A frontend that wants to write ``slot: Modality``
108
+ cannot, unless the name survives generation — and passing them explicitly
109
+ beats inferring which inline unions "look like" a named type.
110
+ """
111
+ defs: dict[str, dict[str, Any]] = {}
112
+ roots: dict[str, dict[str, Any]] = {}
113
+ for schema in schemas.values():
114
+ for name, sub in (schema.get("$defs") or {}).items():
115
+ defs.setdefault(name, sub)
116
+ root = {k: v for k, v in schema.items() if k != "$defs"}
117
+ roots.setdefault(root.get("title") or "Unnamed", root)
118
+
119
+ parts = [
120
+ "// GENERATED by `python -m evalrx.contract.export --out docs/contract`.\n"
121
+ "// Do not edit: Python is the source of truth (evalrx/contract/).\n"
122
+ f"// schema_version {schema_version}\n\n"
123
+ "/** Bumped only when a field's MEANING changes. Unknown fields must be\n"
124
+ " * ignored by readers, never rejected. */\n"
125
+ f"export const SCHEMA_VERSION = {schema_version};\n\n",
126
+ ]
127
+ for name, values in sorted((aliases or {}).items()):
128
+ parts.append(
129
+ f"export type {name} = " + " | ".join(_literal(v) for v in values) + ";\n\n"
130
+ )
131
+ for name in sorted(defs):
132
+ if name in roots:
133
+ continue
134
+ parts.append(_interface(name, defs[name]) + "\n")
135
+ for name in sorted(roots):
136
+ parts.append(_interface(name, roots[name]) + "\n")
137
+ return "".join(parts)
138
+
139
+
140
+ __all__ = ["render"]
@@ -0,0 +1,85 @@
1
+ """EvalRX core — the sklearn-like substrate.
2
+
3
+ Public contracts every other module builds on:
4
+
5
+ Capability the vocabulary connecting models and analyzers
6
+ Model analyzable model: generate() + forward(capture)->Trace
7
+ Analyzer sklearn-style estimator: Analyzer(**params).run(model, data)->Result
8
+ FailureCase the central data unit; CaseBatch is a collection of them
9
+ Result uniform, agent-readable output (findings + artifacts)
10
+ registry discovery: list/match models and analyzers by capability
11
+ Pipeline compose analyzers
12
+ Experiment declarative spec the agent layer targets
13
+ """
14
+
15
+ from evalrx.core.analyzer import Analyzer
16
+ from evalrx.core.capability import Capability, CapabilityError
17
+ from evalrx.core.case import (
18
+ CaseBatch,
19
+ FailureCase,
20
+ Inputs,
21
+ Label,
22
+ Provenance,
23
+ Source,
24
+ Step,
25
+ StepRole,
26
+ Trajectory,
27
+ as_casebatch,
28
+ )
29
+ from evalrx.core.experiment import Experiment, ExperimentRunner
30
+ from evalrx.core.model import CaptureSpec, Model, TokenLogprob, Trace
31
+ from evalrx.core.pipeline import Pipeline
32
+ from evalrx.core.registry import (
33
+ Registry,
34
+ register_analyzer,
35
+ register_model,
36
+ registry,
37
+ )
38
+ from evalrx.core.result import Result
39
+ from evalrx.core.spec import (
40
+ AttnSemantics,
41
+ AudioSpec,
42
+ ModelSpec,
43
+ ModulePaths,
44
+ VisionSpec,
45
+ )
46
+ from evalrx.core.tokentype import TokenTypeMap, build_token_type_map
47
+ from evalrx.core.tool import ChatTurn, Tool, ToolCall
48
+
49
+ __all__ = [
50
+ "Capability",
51
+ "CapabilityError",
52
+ "Model",
53
+ "Trace",
54
+ "TokenLogprob",
55
+ "CaptureSpec",
56
+ "Analyzer",
57
+ "FailureCase",
58
+ "CaseBatch",
59
+ "Inputs",
60
+ "Label",
61
+ "Provenance",
62
+ "Source",
63
+ "Step",
64
+ "StepRole",
65
+ "Trajectory",
66
+ "as_casebatch",
67
+ "ModelSpec",
68
+ "VisionSpec",
69
+ "AudioSpec",
70
+ "ModulePaths",
71
+ "AttnSemantics",
72
+ "Tool",
73
+ "ToolCall",
74
+ "ChatTurn",
75
+ "TokenTypeMap",
76
+ "build_token_type_map",
77
+ "Result",
78
+ "registry",
79
+ "Registry",
80
+ "register_model",
81
+ "register_analyzer",
82
+ "Pipeline",
83
+ "Experiment",
84
+ "ExperimentRunner",
85
+ ]