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,460 @@
1
+ """Natural-language scaffold for the EvalRX diagnosis pipeline.
2
+
3
+ Generates a self-contained Docker experiment from a plain-text description
4
+ of a model failure. The resulting directory mirrors the ``examples/`` layout
5
+ and can be launched immediately::
6
+
7
+ python -m evalrx.eval_agent.nl_runner \\
8
+ --description "My VLM confuses left and right in spatial questions" \\
9
+ --model qwen2.5-vl-7b-instruct \\
10
+ --out ./my_experiment
11
+
12
+ cd my_experiment && docker compose up
13
+
14
+ Two generation modes
15
+ --------------------
16
+ CLI agent (``--provider claude_code / gemini_cli / …``)
17
+ A coding agent writes a bespoke ``run.py`` tailored to the description.
18
+ Requires the corresponding binary and API key.
19
+
20
+ Template (default, no extra binary needed)
21
+ A parametrised template is filled with the description and model key.
22
+ The produced ``run.py`` is a complete, working script that users can
23
+ customise further.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import argparse
29
+ from pathlib import Path
30
+
31
+ from evalrx.eval_agent.prompts.nl_runner import _AGENT_PROMPT_TEMPLATE
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # File templates
35
+ # ---------------------------------------------------------------------------
36
+
37
+ _RUN_PY_TEMPLATE = '''\
38
+ """EvalRX diagnosis experiment — generated from description.
39
+
40
+ Description
41
+ -----------
42
+ {description}
43
+
44
+ Model : {model_key}
45
+
46
+ Usage (via Docker — preferred)
47
+ docker compose up
48
+
49
+ Usage (direct)
50
+ python run.py --smoke-test # quick wiring check, no GPU/API key
51
+ python run.py # full run
52
+
53
+ Edit the CASES list below to add your own failure examples.
54
+ """
55
+
56
+ from __future__ import annotations
57
+
58
+ import argparse
59
+ import sys
60
+ from pathlib import Path
61
+
62
+ _OUTPUTS_DIR = Path(__file__).parent / "outputs"
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Failure cases — EDIT THESE to match your scenario
66
+ # ---------------------------------------------------------------------------
67
+ # Each FailureCase has:
68
+ # id : unique string key
69
+ # inputs : Inputs(prompt=..., image=...) — image is optional
70
+ # expected : what the correct answer looks like (string or rubric dict)
71
+ # ---------------------------------------------------------------------------
72
+
73
+ def _build_cases():
74
+ from evalrx.core.case import CaseBatch, FailureCase, Inputs
75
+
76
+ return CaseBatch([
77
+ # Replace these with real failure cases from your evaluation.
78
+ FailureCase(
79
+ id="example_0",
80
+ inputs=Inputs(prompt="[TODO: replace with a prompt that exposes the failure]"),
81
+ expected="[expected answer]",
82
+ ),
83
+ FailureCase(
84
+ id="example_1",
85
+ inputs=Inputs(prompt="[TODO: replace with a passing control prompt]"),
86
+ expected="[expected answer]",
87
+ ),
88
+ ])
89
+
90
+
91
+ # ---------------------------------------------------------------------------
92
+ # Smoke-test stand-in (no model / GPU required)
93
+ # ---------------------------------------------------------------------------
94
+
95
+ class _SmokeModel:
96
+ def __init__(self):
97
+ from evalrx.core.capability import Capability
98
+ self.capabilities = frozenset({{Capability.GENERATE}})
99
+ self.modalities = frozenset({{"text"}})
100
+
101
+ def generate(self, inputs, **kwargs) -> str:
102
+ return "smoke-test answer"
103
+
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # Main
107
+ # ---------------------------------------------------------------------------
108
+
109
+ def main() -> None:
110
+ parser = argparse.ArgumentParser()
111
+ parser.add_argument("--model", default="{model_key}")
112
+ parser.add_argument("--device", default="cpu")
113
+ parser.add_argument("--dtype", default="float32")
114
+ parser.add_argument("--max-cycles", type=int, default=2)
115
+ parser.add_argument("--max-analyzers", type=int, default=2)
116
+ parser.add_argument("--smoke-test", action="store_true",
117
+ help="Run with a tiny stand-in model (no GPU/API key needed)")
118
+ parser.add_argument("--run-dir", default=str(_OUTPUTS_DIR))
119
+ args = parser.parse_args()
120
+
121
+ import evalrx # noqa: F401 — registers all analyzers
122
+
123
+ from evalrx.eval_agent import AgyModel, RunContext, VLDiagnoseLoop
124
+ from evalrx.eval_agent.stages.diagnosis import DiagnosisAgent
125
+ from evalrx.eval_agent.stages.probe_agent import ProbeAgent
126
+ from evalrx.eval_agent.stages.protocol import ExperimentProtocol
127
+ from evalrx.analysis.stats_agent import StatsAnalysisAgent
128
+
129
+ protocol = ExperimentProtocol(
130
+ description=(
131
+ "{description}"
132
+ ),
133
+ task_domain="{task_domain}",
134
+ success_criteria="The model answer matches the expected output.",
135
+ )
136
+
137
+ if args.smoke_test:
138
+ model = _SmokeModel()
139
+ print("[smoke-test] using stand-in model — skipping real inference")
140
+ else:
141
+ from evalrx import compose
142
+ from evalrx.core.capability import Capability
143
+ model = compose(args.model, "hf_local",
144
+ want={{Capability.GENERATE, Capability.ATTENTION}},
145
+ device=args.device, dtype=args.dtype)
146
+
147
+ cases = _build_cases()
148
+
149
+ try:
150
+ judge = AgyModel()
151
+ except RuntimeError:
152
+ judge = None
153
+ print("[warn] agy binary not found — M3/M4 will be skipped")
154
+
155
+ ctx = RunContext(args.run_dir, verbose=True)
156
+ print(f"Logging to: {{ctx.root}}")
157
+
158
+ loop = VLDiagnoseLoop(
159
+ model=model,
160
+ probe_agent=ProbeAgent(max_analyzers=args.max_analyzers),
161
+ stats_agent=StatsAnalysisAgent(judge=judge, figure_dir=str(ctx.figures_dir)),
162
+ diagnosis_agent=DiagnosisAgent(judge=judge),
163
+ max_cycles=args.max_cycles,
164
+ protocol=protocol,
165
+ run_logger=ctx.logger,
166
+ )
167
+ report = loop.run(cases)
168
+ ctx.write_diagnose_report(report, cases)
169
+ ctx.finalize()
170
+
171
+ print(f"\\n[VLDiagnoseLoop] cycles={{report.cycles}} resolved={{report.resolved}}")
172
+ for h in report.final_hypotheses:
173
+ print(f" hypothesis : {{h.statement}}")
174
+ print(f" status : {{h.status}}")
175
+ print(f"\\nRun record -> {{ctx.root / 'run.json'}}")
176
+
177
+
178
+ if __name__ == "__main__":
179
+ main()
180
+ '''
181
+
182
+ _DOCKERFILE_TEMPLATE = """\
183
+ FROM python:3.11-slim
184
+
185
+ WORKDIR /app
186
+
187
+ # Install torch with CUDA 12.4 wheels first so [local] won't reinstall it
188
+ RUN pip install --no-cache-dir \\
189
+ torch torchvision --index-url https://download.pytorch.org/whl/cu124
190
+
191
+ # Install evalrx from local source
192
+ COPY . /evalrx
193
+ RUN pip install --no-cache-dir "/evalrx[local,gemini,data,viz]"
194
+
195
+ WORKDIR /app/example
196
+ COPY {rel_dir}/ .
197
+
198
+ ENV PYTHONUNBUFFERED=1
199
+
200
+ CMD ["python", "run.py"]
201
+ """
202
+
203
+ _COMPOSE_TEMPLATE = """\
204
+ services:
205
+ evalrx_experiment:
206
+ build:
207
+ context: {build_context}
208
+ dockerfile: {rel_dir}/Dockerfile
209
+ image: evalrx-{slug}
210
+ environment:
211
+ - CUDA_DEVICE_ORDER=PCI_BUS_ID
212
+ - CUDA_VISIBLE_DEVICES=${{CUDA_VISIBLE_DEVICES:-0}}
213
+ - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
214
+ - HF_HOME=/hf_cache
215
+ - HOME=/home/user
216
+ volumes:
217
+ - ${{HF_HOME:-~/.cache/huggingface}}:/hf_cache
218
+ - ./outputs:/app/example/outputs
219
+ - ${{AGY_PATH:-~/.local/bin/agy}}:/usr/local/bin/agy:ro
220
+ - ~/.gemini:/home/user/.gemini
221
+ - ~/.cache/antigravity:/home/user/.cache/antigravity
222
+ deploy:
223
+ resources:
224
+ reservations:
225
+ devices:
226
+ - driver: nvidia
227
+ count: all
228
+ capabilities: [gpu]
229
+ command: >
230
+ python run.py
231
+ --model {model_key}
232
+ --device cuda
233
+ --dtype bfloat16
234
+ --max-cycles 2
235
+ --max-analyzers 2
236
+ """
237
+
238
+ _GITIGNORE = "outputs/\n__pycache__/\n"
239
+
240
+ # ---------------------------------------------------------------------------
241
+ # Public API
242
+ # ---------------------------------------------------------------------------
243
+
244
+ def scaffold_from_description(
245
+ description: str,
246
+ model_key: str = "qwen2.5-vl-7b-instruct",
247
+ output_dir: str | Path = "./my_evalrx_experiment",
248
+ provider: str = "",
249
+ cli_binary: str = "",
250
+ cli_model: str = "",
251
+ cli_timeout: int = 180,
252
+ task_domain: str = "",
253
+ ) -> Path:
254
+ """Scaffold a Docker experiment from a natural-language failure description.
255
+
256
+ Args:
257
+ description: Plain-text description of the failure to diagnose.
258
+ model_key: EvalRX model key (from ``evalrx.list_specs()``).
259
+ output_dir: Where to write the generated scaffold.
260
+ provider: CLI agent provider (``"claude_code"``, ``"gemini_cli"``,
261
+ ``"codex"``, …). Leave empty to use the template path.
262
+ cli_binary: Explicit path to the CLI binary (auto-detected when empty).
263
+ cli_model: Model flag forwarded to the CLI binary (e.g. ``"sonnet"``).
264
+ cli_timeout: Seconds allowed for the CLI agent to write ``run.py``.
265
+ task_domain: Short label for the experiment (e.g. ``"spatial reasoning"``).
266
+
267
+ Returns:
268
+ ``Path`` to the generated scaffold directory.
269
+
270
+ Raises:
271
+ RuntimeError: When ``provider`` is set but the binary is not found.
272
+ """
273
+ out = Path(output_dir).expanduser().resolve()
274
+ out.mkdir(parents=True, exist_ok=True)
275
+
276
+ run_py_path = out / "run.py"
277
+
278
+ if provider:
279
+ _write_run_py_via_agent(
280
+ run_py_path,
281
+ description=description,
282
+ model_key=model_key,
283
+ provider=provider,
284
+ binary=cli_binary,
285
+ model=cli_model,
286
+ timeout=cli_timeout,
287
+ )
288
+ else:
289
+ _write_run_py_template(
290
+ run_py_path,
291
+ description=description,
292
+ model_key=model_key,
293
+ task_domain=task_domain or _infer_domain(description),
294
+ )
295
+
296
+ _write_docker_files(out, model_key=model_key)
297
+ return out
298
+
299
+
300
+ # ---------------------------------------------------------------------------
301
+ # Internal helpers
302
+ # ---------------------------------------------------------------------------
303
+
304
+ def _infer_domain(description: str) -> str:
305
+ """Return a short domain label heuristically extracted from the description."""
306
+ desc_lower = description.lower()
307
+ for keyword in ("spatial", "temporal", "count", "ocr", "grounding",
308
+ "hallucination", "reasoning", "chart", "navigation"):
309
+ if keyword in desc_lower:
310
+ return keyword + " understanding"
311
+ return "visual understanding"
312
+
313
+
314
+ def _write_run_py_template(
315
+ path: Path,
316
+ description: str,
317
+ model_key: str,
318
+ task_domain: str,
319
+ ) -> None:
320
+ escaped_desc = description.replace('"', '\\"')
321
+ content = _RUN_PY_TEMPLATE.format(
322
+ description=escaped_desc,
323
+ model_key=model_key,
324
+ task_domain=task_domain,
325
+ )
326
+ path.write_text(content, encoding="utf-8")
327
+ print(f"[nl_runner] wrote {path}")
328
+ print("[nl_runner] edit CASES in run.py to add your failure examples")
329
+
330
+
331
+ def _write_run_py_via_agent(
332
+ path: Path,
333
+ *,
334
+ description: str,
335
+ model_key: str,
336
+ provider: str,
337
+ binary: str,
338
+ model: str,
339
+ timeout: int,
340
+ ) -> None:
341
+ from evalrx.agent_runtime.cli_types import CliAgentConfig
342
+ from evalrx.agent_runtime.codegen import CodegenRunner
343
+
344
+ prompt = _AGENT_PROMPT_TEMPLATE.format(
345
+ description=description,
346
+ model_key=model_key,
347
+ )
348
+ cfg = CliAgentConfig(
349
+ provider=provider,
350
+ binary_path=binary,
351
+ model=model,
352
+ timeout_sec=timeout,
353
+ )
354
+ result = CodegenRunner(cfg).write_code(
355
+ prompt,
356
+ workdir=path.parent,
357
+ preferred_filenames=("run.py",),
358
+ )
359
+
360
+ if not result.ok:
361
+ raise RuntimeError(
362
+ f"CLI agent ({provider}) failed to generate run.py: {result.error}"
363
+ )
364
+
365
+ generated = result.code
366
+ if generated is None:
367
+ raise RuntimeError(
368
+ f"CLI agent ({provider}) produced no .py files in {path.parent}"
369
+ )
370
+
371
+ path.write_text(generated, encoding="utf-8")
372
+ print(f"[nl_runner] {provider} wrote {path} ({len(result.files)} file(s))")
373
+
374
+
375
+ def _write_docker_files(out: Path, *, model_key: str) -> None:
376
+ slug = model_key.replace(".", "-").replace("/", "-")
377
+
378
+ # Detect whether out is inside the repo (for a relative build context)
379
+ try:
380
+ rel = out.relative_to(Path.cwd())
381
+ rel_dir = str(rel).replace("\\", "/")
382
+ build_context = str(Path.cwd()).replace("\\", "/")
383
+ except ValueError:
384
+ rel_dir = out.name
385
+ build_context = str(out.parent).replace("\\", "/")
386
+
387
+ dockerfile = _DOCKERFILE_TEMPLATE.format(rel_dir=rel_dir)
388
+ compose = _COMPOSE_TEMPLATE.format(
389
+ build_context=build_context,
390
+ rel_dir=rel_dir,
391
+ slug=slug,
392
+ model_key=model_key,
393
+ )
394
+
395
+ (out / "Dockerfile").write_text(dockerfile, encoding="utf-8")
396
+ (out / "docker-compose.yml").write_text(compose, encoding="utf-8")
397
+ (out / ".gitignore").write_text(_GITIGNORE, encoding="utf-8")
398
+ print(f"[nl_runner] scaffold ready in {out}")
399
+ print(f"[nl_runner] cd {out} && docker compose up")
400
+
401
+
402
+ # ---------------------------------------------------------------------------
403
+ # CLI entry point
404
+ # ---------------------------------------------------------------------------
405
+
406
+ def _parse_args() -> argparse.Namespace:
407
+ p = argparse.ArgumentParser(
408
+ description=(
409
+ "Scaffold an EvalRX Docker experiment from a natural-language "
410
+ "description of a model failure."
411
+ )
412
+ )
413
+ p.add_argument(
414
+ "--description", "-d", required=True,
415
+ help="Plain-text description of the failure to diagnose.",
416
+ )
417
+ p.add_argument(
418
+ "--model", "-m", default="qwen2.5-vl-7b-instruct",
419
+ metavar="MODEL_KEY",
420
+ help="EvalRX model key (see evalrx.list_specs()).",
421
+ )
422
+ p.add_argument(
423
+ "--out", "-o", default="./my_evalrx_experiment",
424
+ metavar="DIR",
425
+ help="Output directory for the generated scaffold.",
426
+ )
427
+ p.add_argument(
428
+ "--provider", default="",
429
+ metavar="PROVIDER",
430
+ help=(
431
+ "CLI agent provider for run.py generation "
432
+ "(claude_code / gemini_cli / codex / opencode / kimi_cli). "
433
+ "Omit to use the built-in template."
434
+ ),
435
+ )
436
+ p.add_argument("--cli-model", default="", help="Model flag forwarded to the CLI binary.")
437
+ p.add_argument("--cli-binary", default="", help="Explicit path to the CLI binary.")
438
+ p.add_argument("--cli-timeout", type=int, default=180, help="CLI agent timeout (seconds).")
439
+ p.add_argument("--task-domain", default="", help='Short domain label, e.g. "spatial reasoning".')
440
+ return p.parse_args()
441
+
442
+
443
+ def main() -> None:
444
+ args = _parse_args()
445
+ out = scaffold_from_description(
446
+ description=args.description,
447
+ model_key=args.model,
448
+ output_dir=args.out,
449
+ provider=args.provider,
450
+ cli_binary=args.cli_binary,
451
+ cli_model=args.cli_model,
452
+ cli_timeout=args.cli_timeout,
453
+ task_domain=args.task_domain,
454
+ )
455
+ print(f"\nScaffold created at: {out}")
456
+ print(f" cd {out} && docker compose up")
457
+
458
+
459
+ if __name__ == "__main__":
460
+ main()
@@ -0,0 +1,61 @@
1
+ """Closed-loop orchestrator: define → split → pre-register → test → report.
2
+
3
+ Implements the selective-inference-safe path:
4
+ 1. partition cases into explore/validate/confirm (DataSplit),
5
+ 2. register the hypothesis (hash + timestamp) BEFORE unblinding its test split,
6
+ 3. run the A/B on that split,
7
+ 4. emit a DiagnosticReport carrying the pre-registration proof.
8
+
9
+ The exploratory generation that *produces* hypotheses (LLM-backed) lives in the
10
+ SelfEvolveLoop; this orchestrator enforces the discipline around testing them.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Callable, Optional
16
+
17
+ from evalrx.core.experiment import ExperimentRunner
18
+ from evalrx.eval_agent.ab_runner import ABRunner
19
+ from evalrx.eval_agent.legacy import SelfEvolveLoop
20
+ from evalrx.eval_agent.preregister import (
21
+ DataSplit,
22
+ PreregisteredHypothesis,
23
+ PreregistrationLog,
24
+ Split,
25
+ )
26
+ from evalrx.eval_agent.report import DiagnosticReport
27
+
28
+
29
+ class EvalOrchestrator:
30
+ """Drive a pre-registered A/B comparison end-to-end."""
31
+
32
+ def __init__(self, loop: Optional[SelfEvolveLoop] = None, split: Optional[DataSplit] = None) -> None:
33
+ self.loop = loop or SelfEvolveLoop()
34
+ self.runner: ExperimentRunner = self.loop.runner
35
+ self.split = split or DataSplit()
36
+ self.prereg_log = PreregistrationLog()
37
+
38
+ def run(
39
+ self,
40
+ cases,
41
+ hypothesis: PreregisteredHypothesis,
42
+ strategy_a: Callable,
43
+ strategy_b: Callable,
44
+ *,
45
+ cluster_fn: Optional[Callable] = None,
46
+ timestamp: Optional[str] = None,
47
+ ) -> dict:
48
+ """Register *hypothesis*, test it on its split, return a DiagnosticReport dict."""
49
+ parts = self.split.partition(cases)
50
+ prereg_hash = self.prereg_log.register(hypothesis, timestamp) # BEFORE unblinding
51
+ test_cases = parts[Split(hypothesis.split)]
52
+ ab = ABRunner(strategy_a, strategy_b, cluster_fn=cluster_fn).run(
53
+ test_cases, alpha=hypothesis.alpha, min_effect=hypothesis.min_effect
54
+ )
55
+ return DiagnosticReport.from_comparison(
56
+ ab.stat,
57
+ title=hypothesis.statement or hypothesis.predicate,
58
+ hypothesis=hypothesis,
59
+ prereg_hash=prereg_hash,
60
+ split=hypothesis.split,
61
+ )
@@ -0,0 +1,93 @@
1
+ """Pre-registration + data splitting — the selective-inference safety machine.
2
+
3
+ The closed loop (mine results → propose hypothesis → test) is textbook data
4
+ dredging if the hypothesis is tested on the same data that suggested it. This
5
+ module enforces the discipline that makes the loop's verdicts trustworthy:
6
+
7
+ * :class:`DataSplit` deterministically partitions cases into **explore /
8
+ validate / confirm** (by hash of case id) — mine freely on explore, test a
9
+ pre-registered hypothesis once on validate, lock confirm for the final report.
10
+ * :class:`PreregisteredHypothesis` is a falsifiable, operationalised contract
11
+ {predicate, metric, direction, test, alpha, min_effect, split} fixed BEFORE
12
+ unblinding the test split.
13
+ * :class:`PreregistrationLog` hashes + timestamps the contract so a report can
14
+ prove the hypothesis preceded the test (and counts the denominator for FDR).
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import hashlib
20
+ import json
21
+ from dataclasses import asdict, dataclass, field
22
+ from enum import Enum
23
+ from typing import Iterable, Optional
24
+
25
+
26
+ class Split(str, Enum):
27
+ EXPLORE = "explore"
28
+ VALIDATE = "validate"
29
+ CONFIRM = "confirm"
30
+
31
+
32
+ @dataclass
33
+ class DataSplit:
34
+ """Deterministic explore/validate/confirm assignment by hash of case id."""
35
+
36
+ explore_frac: float = 0.5
37
+ validate_frac: float = 0.3
38
+ seed: int = 0
39
+
40
+ def assign(self, case_id: str) -> Split:
41
+ h = int(hashlib.sha1(f"{self.seed}:{case_id}".encode()).hexdigest(), 16)
42
+ u = (h % 1_000_000) / 1_000_000
43
+ if u < self.explore_frac:
44
+ return Split.EXPLORE
45
+ if u < self.explore_frac + self.validate_frac:
46
+ return Split.VALIDATE
47
+ return Split.CONFIRM
48
+
49
+ def partition(self, cases: Iterable) -> dict:
50
+ out: dict = {Split.EXPLORE: [], Split.VALIDATE: [], Split.CONFIRM: []}
51
+ for c in cases:
52
+ cid = getattr(c, "id", str(c))
53
+ out[self.assign(cid)].append(c)
54
+ return out
55
+
56
+
57
+ @dataclass(frozen=True)
58
+ class PreregisteredHypothesis:
59
+ """A falsifiable contract fixed before unblinding the test split."""
60
+
61
+ predicate: str # operationalised filter over cases (what subset)
62
+ statement: str = "" # human-readable claim
63
+ metric: str = "success"
64
+ direction: str = "B>A" # expected effect sign
65
+ test: str = "mcnemar+evalue"
66
+ alpha: float = 0.05
67
+ min_effect: float = 0.0
68
+ split: str = Split.VALIDATE.value
69
+
70
+ def contract_hash(self) -> str:
71
+ return hashlib.sha1(json.dumps(asdict(self), sort_keys=True).encode()).hexdigest()[:16]
72
+
73
+
74
+ @dataclass
75
+ class PreregistrationLog:
76
+ """Append-only proof that hypotheses were registered before testing."""
77
+
78
+ entries: list = field(default_factory=list)
79
+
80
+ def register(self, hyp: PreregisteredHypothesis, timestamp: Optional[str] = None) -> str:
81
+ if timestamp is None:
82
+ from datetime import datetime
83
+
84
+ timestamp = datetime.now().isoformat(timespec="seconds")
85
+ h = hyp.contract_hash()
86
+ self.entries.append({"hash": h, "timestamp": timestamp, "contract": asdict(hyp)})
87
+ return h
88
+
89
+ def is_registered(self, hyp: PreregisteredHypothesis) -> bool:
90
+ return any(e["hash"] == hyp.contract_hash() for e in self.entries)
91
+
92
+ def __len__(self) -> int:
93
+ return len(self.entries)
@@ -0,0 +1 @@
1
+ """Prompt builders and prompt policies for EvalRX agents."""
@@ -0,0 +1,46 @@
1
+ """Prompt templates for the agentic diagnosis loop's decision judge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ DECISION_PROMPT = """\
6
+ You are the diagnostician driving an automated model-failure investigation.
7
+ Unlike a fixed pipeline, you choose which step to run next based on the
8
+ evidence gathered so far — probe the model, run statistics, explore the raw
9
+ data, propose hypotheses, test a hypothesis, or (once one is statistically
10
+ supported) stop.
11
+
12
+ {evidence}
13
+
14
+ Reply with EXACTLY ONE JSON object choosing your next action — no prose
15
+ outside it:
16
+ {{"tool": "<tool name from AVAILABLE TOOLS>", "params": {{...}}, "rationale": "<one sentence: why this tool, why now>"}}
17
+
18
+ Rules:
19
+ - You may only call "stop" with params.resolved=true once a hypothesis has
20
+ actually been tested and is statistically supported and protocol-consistent
21
+ — declaring success earlier will be rejected and you'll be asked again.
22
+ - Do not repeat a tool past its stated call limit.
23
+ - Do not call a tool that is marked BLOCKED until its listed precondition(s)
24
+ are met — run the blocking step first.
25
+ - Prefer the order that makes evidentiary sense (e.g. you need probe findings
26
+ before stats, stats before hypotheses, hypotheses before testing one) but
27
+ you decide the sequence and when to stop, not a fixed script.
28
+
29
+ Return ONLY the JSON object."""
30
+
31
+ ACTION_REPAIR_PROMPT = """\
32
+ Your previous response could not be used as an action.
33
+
34
+ Previous response:
35
+ {raw}
36
+
37
+ Problem(s):
38
+ {errors}
39
+
40
+ {evidence}
41
+
42
+ Reply with EXACTLY ONE JSON object choosing your next action — no prose
43
+ outside it:
44
+ {{"tool": "<tool name from AVAILABLE TOOLS>", "params": {{...}}, "rationale": "<one sentence>"}}
45
+
46
+ Return ONLY the JSON object."""
@@ -0,0 +1,25 @@
1
+ """Prompt templates for case discovery and labeling."""
2
+
3
+ _JUDGE_PROMPT = """\
4
+ You are scoring one model answer for an evaluation case.
5
+
6
+ Experiment protocol:
7
+ {protocol}
8
+
9
+ Success criteria:
10
+ {success_criteria}
11
+
12
+ Prompt:
13
+ {prompt}
14
+
15
+ Expected answer or rubric:
16
+ {expected}
17
+
18
+ Observed model answer:
19
+ {observed}
20
+
21
+ Return a JSON object:
22
+ {{"label": "PASS|FAIL|UNKNOWN", "reason": "one concise sentence"}}
23
+
24
+ Use PASS only when the observed answer satisfies the expected answer/rubric under
25
+ the success criteria."""