agentinputlab 0.1.0a1__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 (469) hide show
  1. agentinputlab/__init__.py +28 -0
  2. agentinputlab/algorithms/__init__.py +6 -0
  3. agentinputlab/algorithms/acquire/__init__.py +145 -0
  4. agentinputlab/algorithms/acquire/adaptive_rag/__init__.py +43 -0
  5. agentinputlab/algorithms/acquire/adaptive_rag/replay.py +367 -0
  6. agentinputlab/algorithms/acquire/adaptive_rag/runtime.py +1202 -0
  7. agentinputlab/algorithms/acquire/adaptive_rag_common_support.py +542 -0
  8. agentinputlab/algorithms/acquire/hyde.py +296 -0
  9. agentinputlab/algorithms/acquire/hyde_controlled.py +200 -0
  10. agentinputlab/algorithms/acquire/hyde_external.py +148 -0
  11. agentinputlab/algorithms/acquire/ircot.py +28 -0
  12. agentinputlab/algorithms/acquire/ircot_common_support.py +478 -0
  13. agentinputlab/algorithms/acquire/ircot_native.py +355 -0
  14. agentinputlab/algorithms/acquire/ircot_replay.py +327 -0
  15. agentinputlab/algorithms/acquire/qrag/__init__.py +79 -0
  16. agentinputlab/algorithms/acquire/qrag/bridge.py +828 -0
  17. agentinputlab/algorithms/acquire/qrag/common_support.py +891 -0
  18. agentinputlab/algorithms/acquire/qrag/native_axis.py +71 -0
  19. agentinputlab/algorithms/acquire/qrag/reader_adapter.py +139 -0
  20. agentinputlab/algorithms/acquire/r2ag/__init__.py +57 -0
  21. agentinputlab/algorithms/acquire/r2ag/bridge.py +1312 -0
  22. agentinputlab/algorithms/acquire/r2ag/common_support.py +1029 -0
  23. agentinputlab/algorithms/acquire/r2ag/socket_runtime.py +255 -0
  24. agentinputlab/algorithms/clarify/__init__.py +61 -0
  25. agentinputlab/algorithms/clarify/_structured.py +64 -0
  26. agentinputlab/algorithms/clarify/active_task_disambiguation.py +371 -0
  27. agentinputlab/algorithms/clarify/ask_or_assume/__init__.py +17 -0
  28. agentinputlab/algorithms/clarify/ask_or_assume/monitor.py +410 -0
  29. agentinputlab/algorithms/clarify/future_turns.py +379 -0
  30. agentinputlab/algorithms/clarify/udcs.py +255 -0
  31. agentinputlab/algorithms/clarify/udcs_deterministic.py +196 -0
  32. agentinputlab/algorithms/curate/__init__.py +65 -0
  33. agentinputlab/algorithms/curate/acon/__init__.py +27 -0
  34. agentinputlab/algorithms/curate/acon/common.py +284 -0
  35. agentinputlab/algorithms/curate/acon/common_support.py +136 -0
  36. agentinputlab/algorithms/curate/acon/history_bridge.py +309 -0
  37. agentinputlab/algorithms/curate/acon/observation_bridge.py +290 -0
  38. agentinputlab/algorithms/curate/llmlingua/__init__.py +30 -0
  39. agentinputlab/algorithms/curate/llmlingua/adapters.py +537 -0
  40. agentinputlab/algorithms/curate/llmlingua/budget.py +48 -0
  41. agentinputlab/algorithms/curate/llmlingua/controlled.py +175 -0
  42. agentinputlab/algorithms/curate/llmlingua/multilingual.py +105 -0
  43. agentinputlab/algorithms/curate/llmlingua/span_map.py +167 -0
  44. agentinputlab/algorithms/curate/recomp/__init__.py +47 -0
  45. agentinputlab/algorithms/curate/recomp/external.py +1129 -0
  46. agentinputlab/algorithms/curate/recomp/selective.py +304 -0
  47. agentinputlab/algorithms/decompose/__init__.py +107 -0
  48. agentinputlab/algorithms/decompose/decomp/__init__.py +53 -0
  49. agentinputlab/algorithms/decompose/decomp/common_support.py +478 -0
  50. agentinputlab/algorithms/decompose/decomp/planner_component.py +199 -0
  51. agentinputlab/algorithms/decompose/decomp/worker_bridge.py +416 -0
  52. agentinputlab/algorithms/decompose/llm_compiler/__init__.py +35 -0
  53. agentinputlab/algorithms/decompose/llm_compiler/bridge.py +520 -0
  54. agentinputlab/algorithms/decompose/llm_compiler/common_support.py +504 -0
  55. agentinputlab/algorithms/decompose/plan_and_budget/__init__.py +43 -0
  56. agentinputlab/algorithms/decompose/plan_and_budget/cached_soft_budget.py +362 -0
  57. agentinputlab/algorithms/decompose/plan_and_budget/decomposer_component.py +107 -0
  58. agentinputlab/algorithms/decompose/plan_and_budget/full.py +205 -0
  59. agentinputlab/algorithms/decompose/plan_and_budget/hard_cap.py +246 -0
  60. agentinputlab/algorithms/decompose/plan_and_budget/regenerated_soft_budget.py +448 -0
  61. agentinputlab/algorithms/decompose/plan_and_solve/__init__.py +33 -0
  62. agentinputlab/algorithms/decompose/plan_and_solve/plan_component.py +192 -0
  63. agentinputlab/algorithms/decompose/plan_and_solve/ps_prompt.py +127 -0
  64. agentinputlab/algorithms/decompose/self_ask/__init__.py +27 -0
  65. agentinputlab/algorithms/decompose/self_ask/loop.py +362 -0
  66. agentinputlab/algorithms/decompose/self_ask/planner_component.py +117 -0
  67. agentinputlab/algorithms/reformulate/__init__.py +85 -0
  68. agentinputlab/algorithms/reformulate/infocqr/__init__.py +40 -0
  69. agentinputlab/algorithms/reformulate/infocqr/cache_replay.py +362 -0
  70. agentinputlab/algorithms/reformulate/infocqr/external.py +675 -0
  71. agentinputlab/algorithms/reformulate/infocqr/student.py +189 -0
  72. agentinputlab/algorithms/reformulate/query2doc/__init__.py +42 -0
  73. agentinputlab/algorithms/reformulate/query2doc/controlled_live.py +190 -0
  74. agentinputlab/algorithms/reformulate/query2doc/models.py +95 -0
  75. agentinputlab/algorithms/reformulate/query2doc/official_cached.py +335 -0
  76. agentinputlab/algorithms/reformulate/query2doc/querygym_bridge.py +223 -0
  77. agentinputlab/algorithms/reformulate/query2doc/querygym_runtime.py +326 -0
  78. agentinputlab/algorithms/reformulate/reformer/__init__.py +23 -0
  79. agentinputlab/algorithms/reformulate/reformer/external.py +417 -0
  80. agentinputlab/algorithms/reformulate/rrr.py +696 -0
  81. agentinputlab/annotation/__init__.py +5 -0
  82. agentinputlab/annotation/independent.py +428 -0
  83. agentinputlab/annotation/server.py +233 -0
  84. agentinputlab/annotation/static/app.js +121 -0
  85. agentinputlab/annotation/static/index.html +60 -0
  86. agentinputlab/annotation/static/review.css +2 -0
  87. agentinputlab/annotation/static/review.html +10 -0
  88. agentinputlab/annotation/static/review.js +11 -0
  89. agentinputlab/annotation/static/styles.css +55 -0
  90. agentinputlab/annotation/store.py +444 -0
  91. agentinputlab/api.py +368 -0
  92. agentinputlab/artifacts/__init__.py +15 -0
  93. agentinputlab/artifacts/json_file.py +104 -0
  94. agentinputlab/artifacts/manifest.py +98 -0
  95. agentinputlab/artifacts/store.py +157 -0
  96. agentinputlab/backends/__init__.py +98 -0
  97. agentinputlab/backends/embedding/__init__.py +25 -0
  98. agentinputlab/backends/embedding/base.py +145 -0
  99. agentinputlab/backends/embedding/callable.py +75 -0
  100. agentinputlab/backends/embedding/replay.py +129 -0
  101. agentinputlab/backends/llm/__init__.py +31 -0
  102. agentinputlab/backends/llm/base.py +149 -0
  103. agentinputlab/backends/llm/callable.py +108 -0
  104. agentinputlab/backends/llm/decomp_completion.py +88 -0
  105. agentinputlab/backends/llm/final_content_stop.py +72 -0
  106. agentinputlab/backends/llm/gpt_oss_final_channel.py +84 -0
  107. agentinputlab/backends/llm/openai_compatible.py +332 -0
  108. agentinputlab/backends/llm/transformers.py +171 -0
  109. agentinputlab/backends/retrieval/__init__.py +35 -0
  110. agentinputlab/backends/retrieval/base.py +50 -0
  111. agentinputlab/backends/retrieval/dense.py +777 -0
  112. agentinputlab/backends/retrieval/memory.py +119 -0
  113. agentinputlab/backends/retrieval/sqlite_fts.py +155 -0
  114. agentinputlab/backends/user/__init__.py +15 -0
  115. agentinputlab/backends/user/base.py +66 -0
  116. agentinputlab/backends/user/callable.py +54 -0
  117. agentinputlab/backends/user/replay.py +89 -0
  118. agentinputlab/backends/vector.py +38 -0
  119. agentinputlab/benchmarks/__init__.py +246 -0
  120. agentinputlab/benchmarks/acquisition.py +106 -0
  121. agentinputlab/benchmarks/ale.py +540 -0
  122. agentinputlab/benchmarks/appworld_ul.py +669 -0
  123. agentinputlab/benchmarks/assets.py +226 -0
  124. agentinputlab/benchmarks/base.py +68 -0
  125. agentinputlab/benchmarks/beir_archive.py +235 -0
  126. agentinputlab/benchmarks/beir_sample.py +269 -0
  127. agentinputlab/benchmarks/bfcl_native.py +245 -0
  128. agentinputlab/benchmarks/bfcl_v3.py +607 -0
  129. agentinputlab/benchmarks/cache.py +218 -0
  130. agentinputlab/benchmarks/clariq.py +203 -0
  131. agentinputlab/benchmarks/data/registry.json +321 -0
  132. agentinputlab/benchmarks/data/smoke_assets.json +511 -0
  133. agentinputlab/benchmarks/data/statebench_v0_8_1_task_ids.json +475 -0
  134. agentinputlab/benchmarks/data/toolsandbox_165848b_scenario_names.json +1 -0
  135. agentinputlab/benchmarks/execution.py +1111 -0
  136. agentinputlab/benchmarks/interactive.py +257 -0
  137. agentinputlab/benchmarks/ir_query.py +204 -0
  138. agentinputlab/benchmarks/longbench.py +203 -0
  139. agentinputlab/benchmarks/paired_replay.py +479 -0
  140. agentinputlab/benchmarks/preprocess.py +2313 -0
  141. agentinputlab/benchmarks/production.py +236 -0
  142. agentinputlab/benchmarks/qa.py +963 -0
  143. agentinputlab/benchmarks/registry.py +110 -0
  144. agentinputlab/benchmarks/retrieval.py +459 -0
  145. agentinputlab/benchmarks/runtime.py +509 -0
  146. agentinputlab/benchmarks/statebench.py +938 -0
  147. agentinputlab/benchmarks/tau3.py +725 -0
  148. agentinputlab/benchmarks/toolsandbox.py +626 -0
  149. agentinputlab/benchmarks/trec_dl_sample.py +263 -0
  150. agentinputlab/campaign.py +654 -0
  151. agentinputlab/campaign_runner.py +953 -0
  152. agentinputlab/cli/__init__.py +6 -0
  153. agentinputlab/cli/main.py +933 -0
  154. agentinputlab/connectors/__init__.py +46 -0
  155. agentinputlab/connectors/knowledge/__init__.py +4 -0
  156. agentinputlab/connectors/tools/__init__.py +30 -0
  157. agentinputlab/connectors/tools/base.py +57 -0
  158. agentinputlab/connectors/tools/mcp.py +222 -0
  159. agentinputlab/connectors/tools/memory.py +41 -0
  160. agentinputlab/connectors/tools/openapi.py +695 -0
  161. agentinputlab/connectors/tools/schema.py +102 -0
  162. agentinputlab/contracts/__init__.py +91 -0
  163. agentinputlab/contracts/availability.py +24 -0
  164. agentinputlab/contracts/dependency.py +58 -0
  165. agentinputlab/contracts/enums.py +82 -0
  166. agentinputlab/contracts/errors.py +63 -0
  167. agentinputlab/contracts/evidence.py +43 -0
  168. agentinputlab/contracts/intent.py +110 -0
  169. agentinputlab/contracts/lifecycle.py +164 -0
  170. agentinputlab/contracts/patch.py +167 -0
  171. agentinputlab/contracts/serialization.py +93 -0
  172. agentinputlab/contracts/state.py +127 -0
  173. agentinputlab/contracts/trace.py +59 -0
  174. agentinputlab/contracts/worker.py +42 -0
  175. agentinputlab/core/__init__.py +7 -0
  176. agentinputlab/data/__init__.py +32 -0
  177. agentinputlab/data/checksums.py +9 -0
  178. agentinputlab/data/downloader.py +235 -0
  179. agentinputlab/data/http.py +171 -0
  180. agentinputlab/data/index.py +21 -0
  181. agentinputlab/data/overlap.py +28 -0
  182. agentinputlab/data/preprocess.py +32 -0
  183. agentinputlab/data/storage.py +57 -0
  184. agentinputlab/data/trec_sample.py +427 -0
  185. agentinputlab/evaluation/__init__.py +5 -0
  186. agentinputlab/evaluation/outcomes.py +32 -0
  187. agentinputlab/experiments/__init__.py +278 -0
  188. agentinputlab/experiments/analysis.py +981 -0
  189. agentinputlab/experiments/artifacts.py +687 -0
  190. agentinputlab/experiments/automatic_measurement.py +678 -0
  191. agentinputlab/experiments/auxiliary_matrix.py +203 -0
  192. agentinputlab/experiments/bindings.py +430 -0
  193. agentinputlab/experiments/blueprints.py +147 -0
  194. agentinputlab/experiments/builders/__init__.py +45 -0
  195. agentinputlab/experiments/builders/executable_external_payload.py +423 -0
  196. agentinputlab/experiments/builders/external_process_arena.py +1018 -0
  197. agentinputlab/experiments/builders/registry_intervention.py +463 -0
  198. agentinputlab/experiments/cli.py +158 -0
  199. agentinputlab/experiments/controls.py +340 -0
  200. agentinputlab/experiments/driver_guard.py +205 -0
  201. agentinputlab/experiments/drivers/__init__.py +89 -0
  202. agentinputlab/experiments/drivers/external_arena.py +2908 -0
  203. agentinputlab/experiments/drivers/method_process.py +662 -0
  204. agentinputlab/experiments/drivers/parity_process.py +1484 -0
  205. agentinputlab/experiments/drivers/static_production.py +1574 -0
  206. agentinputlab/experiments/executor.py +448 -0
  207. agentinputlab/experiments/loader.py +605 -0
  208. agentinputlab/experiments/measurement.py +675 -0
  209. agentinputlab/experiments/paper_analysis.py +1818 -0
  210. agentinputlab/experiments/parity_identity.py +605 -0
  211. agentinputlab/experiments/plan.py +277 -0
  212. agentinputlab/experiments/production_builders.py +3739 -0
  213. agentinputlab/experiments/readiness.py +432 -0
  214. agentinputlab/experiments/schema.py +514 -0
  215. agentinputlab/experiments/secrets.py +177 -0
  216. agentinputlab/experiments/stateful_analysis.py +1738 -0
  217. agentinputlab/experiments/transfer_analysis.py +1316 -0
  218. agentinputlab/experiments/unseen_analysis.py +1025 -0
  219. agentinputlab/integrations/__init__.py +1 -0
  220. agentinputlab/integrations/action_space/__init__.py +1 -0
  221. agentinputlab/integrations/external_workers/__init__.py +76 -0
  222. agentinputlab/integrations/external_workers/ace.py +431 -0
  223. agentinputlab/integrations/external_workers/base.py +181 -0
  224. agentinputlab/integrations/external_workers/decomp_live.py +249 -0
  225. agentinputlab/integrations/external_workers/decomp_planner.py +30 -0
  226. agentinputlab/integrations/external_workers/infocqr_student.py +280 -0
  227. agentinputlab/integrations/external_workers/ircot.py +576 -0
  228. agentinputlab/integrations/external_workers/llmcompiler_live.py +360 -0
  229. agentinputlab/integrations/external_workers/longllmlingua.py +162 -0
  230. agentinputlab/integrations/external_workers/method.py +314 -0
  231. agentinputlab/integrations/external_workers/process.py +678 -0
  232. agentinputlab/integrations/external_workers/supervisor.py +1071 -0
  233. agentinputlab/integrations/full_architecture/__init__.py +1 -0
  234. agentinputlab/integrations/full_architecture/hyde.py +212 -0
  235. agentinputlab/integrations/full_architecture/plan_and_budget.py +188 -0
  236. agentinputlab/integrations/live_history.py +222 -0
  237. agentinputlab/integrations/prompt_optimization/__init__.py +1 -0
  238. agentinputlab/integrations/training/__init__.py +1 -0
  239. agentinputlab/metrics/__init__.py +50 -0
  240. agentinputlab/metrics/catalog.py +313 -0
  241. agentinputlab/metrics/harm.py +95 -0
  242. agentinputlab/metrics/paired.py +40 -0
  243. agentinputlab/metrics/qa.py +76 -0
  244. agentinputlab/metrics/text.py +33 -0
  245. agentinputlab/metrics/third_party.py +161 -0
  246. agentinputlab/models/__init__.py +4 -0
  247. agentinputlab/observability/__init__.py +56 -0
  248. agentinputlab/observability/budget.py +316 -0
  249. agentinputlab/observability/instrumentation.py +754 -0
  250. agentinputlab/observability/models.py +403 -0
  251. agentinputlab/observability/monitor.py +380 -0
  252. agentinputlab/optimizers/__init__.py +1 -0
  253. agentinputlab/optimizers/clarify/__init__.py +12 -0
  254. agentinputlab/optimizers/context/__init__.py +22 -0
  255. agentinputlab/optimizers/decompose/__init__.py +64 -0
  256. agentinputlab/optimizers/evidence/__init__.py +22 -0
  257. agentinputlab/optimizers/rewrite/__init__.py +30 -0
  258. agentinputlab/parity/__init__.py +5 -0
  259. agentinputlab/parity/base.py +206 -0
  260. agentinputlab/production_campaign.py +763 -0
  261. agentinputlab/py.typed +1 -0
  262. agentinputlab/recipes/__init__.py +333 -0
  263. agentinputlab/recipes/ace.py +418 -0
  264. agentinputlab/recipes/ace_bfcl.py +273 -0
  265. agentinputlab/recipes/ace_common_support.py +169 -0
  266. agentinputlab/recipes/ace_native_plan.py +322 -0
  267. agentinputlab/recipes/ace_online.py +711 -0
  268. agentinputlab/recipes/ace_retrieval.py +330 -0
  269. agentinputlab/recipes/ace_statebench.py +382 -0
  270. agentinputlab/recipes/ace_tau.py +463 -0
  271. agentinputlab/recipes/ace_toolsandbox.py +432 -0
  272. agentinputlab/recipes/acon_guideline.py +22 -0
  273. agentinputlab/recipes/adaptive_rag.py +822 -0
  274. agentinputlab/recipes/llmlingua2_inference.py +146 -0
  275. agentinputlab/recipes/llmlingua2_training.py +796 -0
  276. agentinputlab/recipes/mce.py +1486 -0
  277. agentinputlab/recipes/mce_final.py +335 -0
  278. agentinputlab/recipes/mce_text_fit.py +499 -0
  279. agentinputlab/recipes/mce_text_only.py +298 -0
  280. agentinputlab/recipes/qrag.py +729 -0
  281. agentinputlab/recipes/qrag_inference.py +364 -0
  282. agentinputlab/recipes/r2ag_features.py +236 -0
  283. agentinputlab/recipes/r2ag_inference.py +503 -0
  284. agentinputlab/recipes/r2ag_training.py +948 -0
  285. agentinputlab/recipes/recomp_abstractive.py +592 -0
  286. agentinputlab/recipes/recomp_extractive.py +525 -0
  287. agentinputlab/recipes/reformer.py +17 -0
  288. agentinputlab/recipes/rqrag.py +1516 -0
  289. agentinputlab/recipes/rqrag_common_support.py +193 -0
  290. agentinputlab/recipes/rqrag_completion.py +147 -0
  291. agentinputlab/recipes/skillopt.py +462 -0
  292. agentinputlab/recipes/skillopt_common_support.py +154 -0
  293. agentinputlab/recipes/skillopt_environment.py +165 -0
  294. agentinputlab/recipes/skillopt_environment_cli.py +5 -0
  295. agentinputlab/recipes/skillopt_retrieval.py +188 -0
  296. agentinputlab/recipes/smartsearch.py +897 -0
  297. agentinputlab/recipes/smartsearch_curriculum.py +283 -0
  298. agentinputlab/recipes/smartsearch_grpo_contracts.py +58 -0
  299. agentinputlab/recipes/smartsearch_grpo_data.py +314 -0
  300. agentinputlab/recipes/smartsearch_grpo_meter.py +324 -0
  301. agentinputlab/recipes/smartsearch_grpo_prefix.py +144 -0
  302. agentinputlab/recipes/smartsearch_grpo_rollout.py +119 -0
  303. agentinputlab/recipes/smartsearch_grpo_tools.py +364 -0
  304. agentinputlab/recipes/smartsearch_grpo_training.py +305 -0
  305. agentinputlab/recipes/smartsearch_grpo_transport.py +248 -0
  306. agentinputlab/recipes/smartsearch_grpo_worker.py +364 -0
  307. agentinputlab/recipes/smartsearch_inference.py +524 -0
  308. agentinputlab/recipes/smartsearch_native_data.py +172 -0
  309. agentinputlab/recipes/smartsearch_policy.py +616 -0
  310. agentinputlab/recipes/smartsearch_policy_worker.py +9 -0
  311. agentinputlab/recipes/smartsearch_sampling.py +232 -0
  312. agentinputlab/recipes/smartsearch_teacher.py +203 -0
  313. agentinputlab/recipes/smartsearch_training.py +265 -0
  314. agentinputlab/registry/__init__.py +46 -0
  315. agentinputlab/registry/beta.py +727 -0
  316. agentinputlab/registry/data/algorithms.json +1959 -0
  317. agentinputlab/registry/data/auxiliary_method_profiles.json +34 -0
  318. agentinputlab/registry/loader.py +100 -0
  319. agentinputlab/registry/result_manifest.py +184 -0
  320. agentinputlab/registry/runtime.py +195 -0
  321. agentinputlab/registry/schema.py +449 -0
  322. agentinputlab/runner/__init__.py +30 -0
  323. agentinputlab/runner/balance.py +248 -0
  324. agentinputlab/runner/cache.py +135 -0
  325. agentinputlab/runner/composite.py +130 -0
  326. agentinputlab/runner/engine.py +285 -0
  327. agentinputlab/runner/runtime_guard.py +268 -0
  328. agentinputlab/runner/worker.py +80 -0
  329. agentinputlab/runtime_bootstrap.py +288 -0
  330. agentinputlab/selectors/__init__.py +196 -0
  331. agentinputlab/selectors/advanced.py +1514 -0
  332. agentinputlab/selectors/artifacts.py +1087 -0
  333. agentinputlab/selectors/base.py +112 -0
  334. agentinputlab/selectors/campaigns.py +1893 -0
  335. agentinputlab/selectors/catalog.py +86 -0
  336. agentinputlab/selectors/discovery.py +1860 -0
  337. agentinputlab/selectors/evaluation.py +1348 -0
  338. agentinputlab/selectors/features.py +383 -0
  339. agentinputlab/selectors/learned.py +502 -0
  340. agentinputlab/selectors/no_op.py +28 -0
  341. agentinputlab/selectors/outcome_table.py +475 -0
  342. agentinputlab/selectors/producer.py +853 -0
  343. agentinputlab/selectors/rules.py +109 -0
  344. agentinputlab/selectors/runtime.py +420 -0
  345. agentinputlab/studies/__init__.py +16 -0
  346. agentinputlab/studies/design.py +165 -0
  347. agentinputlab/studies/paired.py +322 -0
  348. agentinputlab/studio/__init__.py +6 -0
  349. agentinputlab/studio/campaigns.py +819 -0
  350. agentinputlab/studio/engine.py +1408 -0
  351. agentinputlab/studio/model_backends.py +425 -0
  352. agentinputlab/studio/prepared.py +410 -0
  353. agentinputlab/studio/server.py +613 -0
  354. agentinputlab/studio/static/app.js +397 -0
  355. agentinputlab/studio/static/campaigns.html +175 -0
  356. agentinputlab/studio/static/campaigns.js +426 -0
  357. agentinputlab/studio/static/index.html +192 -0
  358. agentinputlab/studio/static/result.html +103 -0
  359. agentinputlab/studio/static/result.js +273 -0
  360. agentinputlab/studio/static/styles.css +467 -0
  361. agentinputlab-0.1.0a1.data/data/share/agentinputlab/CITATION.cff +22 -0
  362. agentinputlab-0.1.0a1.dist-info/METADATA +392 -0
  363. agentinputlab-0.1.0a1.dist-info/RECORD +469 -0
  364. agentinputlab-0.1.0a1.dist-info/WHEEL +5 -0
  365. agentinputlab-0.1.0a1.dist-info/entry_points.txt +6 -0
  366. agentinputlab-0.1.0a1.dist-info/licenses/LICENSE +21 -0
  367. agentinputlab-0.1.0a1.dist-info/licenses/NOTICE +31 -0
  368. agentinputlab-0.1.0a1.dist-info/licenses/THIRD_PARTY_NOTICES.md +94 -0
  369. agentinputlab-0.1.0a1.dist-info/top_level.txt +2 -0
  370. workers/README.md +94 -0
  371. workers/ace/README.md +261 -0
  372. workers/ace/__init__.py +1 -0
  373. workers/ace/compatibility_manifest.json +39 -0
  374. workers/ace/driver.py +1682 -0
  375. workers/acon/README.md +215 -0
  376. workers/acon/__init__.py +5 -0
  377. workers/acon/compatibility_manifest.json +93 -0
  378. workers/acon/driver.py +1859 -0
  379. workers/acon/fitted.py +395 -0
  380. workers/acon/guideline.py +470 -0
  381. workers/acon/guideline_source_manifest.json +20 -0
  382. workers/adaptive_rag/README.md +96 -0
  383. workers/adaptive_rag/__init__.py +15 -0
  384. workers/adaptive_rag/compatibility_manifest.json +61 -0
  385. workers/adaptive_rag/driver.py +1470 -0
  386. workers/adaptive_rag/fixture_runtime.py +163 -0
  387. workers/adaptive_rag/runtime.py +252 -0
  388. workers/ask_or_assume/README.md +124 -0
  389. workers/ask_or_assume/__init__.py +1 -0
  390. workers/ask_or_assume/compatibility_manifest.json +36 -0
  391. workers/ask_or_assume/driver.py +1227 -0
  392. workers/decomp/README.md +133 -0
  393. workers/decomp/UPSTREAM_LICENSE.Apache-2.0.txt +201 -0
  394. workers/decomp/__init__.py +5 -0
  395. workers/decomp/compatibility_manifest.json +205 -0
  396. workers/decomp/driver.py +1015 -0
  397. workers/decomp/live.py +137 -0
  398. workers/decomp/runtime.py +231 -0
  399. workers/infocqr/README.md +106 -0
  400. workers/infocqr/__init__.py +1 -0
  401. workers/infocqr/compatibility_manifest.json +84 -0
  402. workers/infocqr/driver.py +1766 -0
  403. workers/ircot/README.md +167 -0
  404. workers/ircot/__init__.py +7 -0
  405. workers/ircot/compatibility_manifest.json +121 -0
  406. workers/ircot/controlled.py +457 -0
  407. workers/ircot/driver.py +1373 -0
  408. workers/ircot/fixture_bindings.json +39 -0
  409. workers/ircot/fixture_config.json +48 -0
  410. workers/ircot/fixture_prompt.txt +2 -0
  411. workers/ircot/native_qa.py +398 -0
  412. workers/ircot/qa_compatibility_manifest.json +57 -0
  413. workers/ircot/runtime.py +97 -0
  414. workers/llmcompiler/README.md +160 -0
  415. workers/llmcompiler/__init__.py +1 -0
  416. workers/llmcompiler/compatibility_manifest.json +45 -0
  417. workers/llmcompiler/driver.py +1514 -0
  418. workers/llmcompiler/export_prompts.py +74 -0
  419. workers/llmcompiler/live.py +174 -0
  420. workers/llmcompiler/patch_manifest.json +147 -0
  421. workers/mce/README.md +198 -0
  422. workers/mce/__init__.py +29 -0
  423. workers/mce/compatibility_manifest.json +115 -0
  424. workers/mce/driver.py +2585 -0
  425. workers/mce/final_eval.py +173 -0
  426. workers/official_arenas/README.md +25 -0
  427. workers/official_arenas/__init__.py +1 -0
  428. workers/official_arenas/capture_records.py +110 -0
  429. workers/official_arenas/driver.py +619 -0
  430. workers/official_arenas/protocol.py +334 -0
  431. workers/official_arenas/providers/__init__.py +1 -0
  432. workers/official_arenas/providers/_live_history.py +228 -0
  433. workers/official_arenas/providers/_message_compat.py +77 -0
  434. workers/official_arenas/providers/appworld_ul.py +196 -0
  435. workers/official_arenas/providers/fixture.py +112 -0
  436. workers/official_arenas/providers/statebench.py +960 -0
  437. workers/official_arenas/providers/tau3.py +1672 -0
  438. workers/official_arenas/providers/tau_retail.py +586 -0
  439. workers/official_arenas/providers/toolsandbox.py +1321 -0
  440. workers/official_arenas/providers/tooltalk.py +699 -0
  441. workers/official_arenas/retail_capture.py +188 -0
  442. workers/official_arenas/retail_driver.py +275 -0
  443. workers/qrag/README.md +172 -0
  444. workers/qrag/__init__.py +1 -0
  445. workers/qrag/compatibility_manifest.json +64 -0
  446. workers/qrag/driver.py +673 -0
  447. workers/r2ag/README.md +154 -0
  448. workers/r2ag/__init__.py +5 -0
  449. workers/r2ag/compatibility_manifest.json +55 -0
  450. workers/r2ag/driver.py +1287 -0
  451. workers/r2ag/feature_schema.json +8 -0
  452. workers/r2ag/runtime.py +589 -0
  453. workers/recomp/README.md +120 -0
  454. workers/recomp/__init__.py +1 -0
  455. workers/recomp/compatibility_manifest.json +43 -0
  456. workers/recomp/driver.py +1653 -0
  457. workers/reformer/README.md +125 -0
  458. workers/reformer/__init__.py +1 -0
  459. workers/reformer/compatibility_manifest.json +68 -0
  460. workers/reformer/driver.py +1459 -0
  461. workers/reformer/induction.py +457 -0
  462. workers/rrr/README.md +126 -0
  463. workers/rrr/__init__.py +1 -0
  464. workers/rrr/compatibility_manifest.json +57 -0
  465. workers/rrr/driver.py +1133 -0
  466. workers/smartsearch/README.md +331 -0
  467. workers/smartsearch/__init__.py +1 -0
  468. workers/smartsearch/compatibility_manifest.json +41 -0
  469. workers/smartsearch/driver.py +1643 -0
@@ -0,0 +1,1348 @@
1
+ """Outcome-blind selector decisions and verified counterfactual result assembly.
2
+
3
+ This module intentionally separates two capabilities:
4
+
5
+ * :func:`build_selector_evaluation_input` exports only authenticated pre-action
6
+ feature observations, eligibility masks, and content identities.
7
+ * :func:`assemble_selector_campaign_results` is the only function here that
8
+ reads evaluator outcomes, and it accepts an already frozen decision artifact.
9
+
10
+ Consequently a selector policy never receives test outcomes through this API.
11
+ The diagnostic observed-action oracle is created only in the second phase and
12
+ is rejected from the outcome-blind decision artifact.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import random
18
+ import re
19
+ from collections import defaultdict
20
+ from collections.abc import Mapping, Sequence
21
+ from pathlib import Path
22
+ from statistics import fmean
23
+ from typing import Any
24
+
25
+ from agentinputlab.contracts.enums import SemanticFamily
26
+ from agentinputlab.contracts.errors import ArtifactError, ValidationError
27
+ from agentinputlab.contracts.serialization import content_hash, freeze_mapping, to_jsonable
28
+ from agentinputlab.contracts.state import FrozenAgentState
29
+ from agentinputlab.experiments import (
30
+ ExperimentManifest,
31
+ ExperimentStage,
32
+ load_experiment_manifest,
33
+ load_verified_cell_results,
34
+ verify_experiment_run_directory,
35
+ )
36
+
37
+ from .artifacts import SelectorArtifactBundle, SelectorArtifactStage
38
+ from .campaigns import (
39
+ validate_representative_manifest,
40
+ validate_selector_campaign_design,
41
+ validate_selector_campaign_manifest,
42
+ validate_selector_campaign_results,
43
+ )
44
+ from .discovery import (
45
+ _convert_result_record,
46
+ _observation_from_artifact,
47
+ _observation_to_artifact,
48
+ _state_from_artifact,
49
+ _validate_pre_action_receipt,
50
+ )
51
+ from .features import VisibleStateFeatureExtractor
52
+ from .outcome_table import EvaluatedArmOutcome, UtilityDefinition
53
+
54
+ _INPUT_ARTIFACT = "agentinputlab.selector_evaluation_input"
55
+ _DECISION_ARTIFACT = "agentinputlab.selector_campaign_decisions"
56
+ _ZERO_SHOT_ARTIFACT = "agentinputlab.selector_zero_shot_replay"
57
+ _SHA256 = re.compile(r"^[0-9a-f]{64}$")
58
+ _CORE_FAMILIES = (
59
+ SemanticFamily.REFORMULATE,
60
+ SemanticFamily.DECOMPOSE,
61
+ SemanticFamily.ACQUIRE,
62
+ SemanticFamily.CLARIFY,
63
+ SemanticFamily.CURATE,
64
+ )
65
+ _POLICY_ARTIFACT_IDS = {
66
+ "defect_rule",
67
+ "zero_shot_router",
68
+ "penalized_t_learner",
69
+ "gbdt_t_learner",
70
+ "eligibility_aware_selector",
71
+ }
72
+ _STANDARD_SELECTOR_KINDS = {
73
+ "rule",
74
+ "ridge_utility",
75
+ "ridge_t_learner",
76
+ "logistic_t_learner",
77
+ "gbdt_t_learner",
78
+ "knn_utility",
79
+ "cost_risk_guardrail",
80
+ }
81
+
82
+
83
+ def build_selector_evaluation_input(
84
+ run_dirs: Sequence[str | Path],
85
+ *,
86
+ design: Mapping[str, Any],
87
+ representatives: Mapping[str, Any],
88
+ extractor: VisibleStateFeatureExtractor,
89
+ expected_partial_plan_hash: str,
90
+ ) -> Mapping[str, Any]:
91
+ """Export the frozen outcome-blind selector test input.
92
+
93
+ The formal paper design is 526 three-arena common-support groups. The
94
+ older 500-common + 100-partial layout remains accepted for non-paper
95
+ library studies.
96
+ """
97
+
98
+ roots = tuple(Path(item).expanduser().resolve() for item in run_dirs)
99
+ if not roots:
100
+ raise ValidationError("selector evaluation input requires verified run directories")
101
+ parsed_design = validate_selector_campaign_design(design)
102
+ frozen_representatives = validate_representative_manifest(representatives)
103
+ _require_sha256(expected_partial_plan_hash, label="partial-support plan hash")
104
+ if not isinstance(extractor, VisibleStateFeatureExtractor):
105
+ raise TypeError("selector evaluation requires VisibleStateFeatureExtractor")
106
+ rank_one = _rank_one_releases(frozen_representatives)
107
+
108
+ source_runs: list[dict[str, str]] = []
109
+ groups: dict[str, dict[str, Any]] = {}
110
+ seen_cells: set[tuple[str, str]] = set()
111
+ common_run_count = 0
112
+ partial_run_count = 0
113
+ for root in roots:
114
+ manifest, records, source = _verified_source(root)
115
+ common_support = _validate_evaluation_manifest(
116
+ manifest,
117
+ representative_manifest_hash=frozen_representatives["manifest_hash"],
118
+ rank_one=rank_one,
119
+ expected_partial_plan_hash=expected_partial_plan_hash,
120
+ )
121
+ common_run_count += int(common_support)
122
+ partial_run_count += int(not common_support)
123
+ source_runs.append(source)
124
+ arm_by_id = {arm.arm_id: arm for arm in manifest.arms}
125
+ for record in records:
126
+ pre_action = _pre_action_record(
127
+ record,
128
+ arm_by_id=arm_by_id,
129
+ extractor=extractor,
130
+ common_support=common_support,
131
+ )
132
+ group_id = pre_action["group_id"]
133
+ cell_key = (group_id, pre_action["model_slot"])
134
+ if cell_key in seen_cells:
135
+ raise ValidationError("selector evaluation contains duplicate group/model cells")
136
+ seen_cells.add(cell_key)
137
+ existing = groups.get(group_id)
138
+ if existing is None:
139
+ groups[group_id] = {
140
+ "group_id": group_id,
141
+ "suite_id": pre_action["suite_id"],
142
+ "common_support": common_support,
143
+ "state_hash": pre_action["state_hash"],
144
+ "observation": pre_action["observation"],
145
+ "eligible_families": pre_action["eligible_families"],
146
+ "model_slots": [pre_action["model_slot"]],
147
+ "seed": pre_action["seed"],
148
+ }
149
+ continue
150
+ comparable = {
151
+ key: existing[key]
152
+ for key in (
153
+ "suite_id",
154
+ "common_support",
155
+ "state_hash",
156
+ "observation",
157
+ "eligible_families",
158
+ "seed",
159
+ )
160
+ }
161
+ if comparable != {
162
+ "suite_id": pre_action["suite_id"],
163
+ "common_support": common_support,
164
+ "state_hash": pre_action["state_hash"],
165
+ "observation": pre_action["observation"],
166
+ "eligible_families": pre_action["eligible_families"],
167
+ "seed": pre_action["seed"],
168
+ }:
169
+ raise ValidationError(
170
+ "selector group changed pre-action state, features, eligibility, or suite"
171
+ )
172
+ existing["model_slots"].append(pre_action["model_slot"])
173
+
174
+ expected_groups = parsed_design["required_independent_groups"]
175
+ expected_common = parsed_design["minimum_common_support_groups"]
176
+ common_groups = sum(bool(group["common_support"]) for group in groups.values())
177
+ if len(groups) != expected_groups or common_groups != expected_common:
178
+ raise ValidationError(
179
+ "selector evaluation support differs from the frozen design "
180
+ f"({len(groups)} total/{common_groups} common)"
181
+ )
182
+ if set(group["suite_id"] for group in groups.values()) != set(parsed_design["suite_ids"]):
183
+ raise ValidationError("selector evaluation suites differ from the frozen design")
184
+ expected_model_slots = ["anchor_a", "anchor_b"]
185
+ for group in groups.values():
186
+ group["model_slots"] = sorted(group["model_slots"])
187
+ if (
188
+ group["model_slots"] != expected_model_slots
189
+ or group["seed"] != 17
190
+ or len(group["eligible_families"]) != (5 if group["common_support"] else 3)
191
+ ):
192
+ raise ValidationError(
193
+ "selector evaluation group differs from the two-model, one-seed "
194
+ "common/partial support protocol"
195
+ )
196
+ if expected_groups == 600 and expected_common == 500:
197
+ if common_run_count != 1 or partial_run_count != 10:
198
+ raise ValidationError(
199
+ "600-group selector evaluation requires one common and ten partial runs"
200
+ )
201
+ partial_groups = expected_groups - expected_common
202
+ if partial_groups != 100:
203
+ raise ValidationError("selector partial-support block must contain 100 groups")
204
+
205
+ design_payload = _jsonable_mapping(parsed_design)
206
+ source_payload = sorted(source_runs, key=lambda item: item["run_identity_hash"])
207
+ group_payload = [groups[group_id] for group_id in sorted(groups)]
208
+ base = {
209
+ "schema_version": 1,
210
+ "artifact": _INPUT_ARTIFACT,
211
+ "stage": "frozen",
212
+ "data_status": "VERIFIED_PRE_ACTION_INPUTS",
213
+ "is_synthetic": False,
214
+ "campaign_design": design_payload,
215
+ "campaign_design_hash": content_hash(design_payload),
216
+ "representative_manifest_hash": frozen_representatives["manifest_hash"],
217
+ "partial_support_plan_hash": expected_partial_plan_hash,
218
+ "source_runs": source_payload,
219
+ "extractor": {
220
+ "id": extractor.extractor_id,
221
+ "revision": extractor.extractor_revision,
222
+ "sha256": extractor.extractor_hash,
223
+ "feature_schema_id": extractor.feature_schema_id,
224
+ "feature_schema_sha256": extractor.feature_schema_hash,
225
+ },
226
+ "groups": group_payload,
227
+ "outcomes_included": False,
228
+ "other_arm_outcomes_accessible": False,
229
+ }
230
+ artifact = {**base, "input_hash": content_hash(base)}
231
+ return validate_selector_evaluation_input(artifact)
232
+
233
+
234
+ def validate_selector_evaluation_input(
235
+ value: Mapping[str, Any],
236
+ ) -> Mapping[str, Any]:
237
+ """Validate and freeze an outcome-free selector evaluation input."""
238
+
239
+ required = {
240
+ "schema_version",
241
+ "artifact",
242
+ "stage",
243
+ "data_status",
244
+ "is_synthetic",
245
+ "campaign_design",
246
+ "campaign_design_hash",
247
+ "representative_manifest_hash",
248
+ "partial_support_plan_hash",
249
+ "source_runs",
250
+ "extractor",
251
+ "groups",
252
+ "outcomes_included",
253
+ "other_arm_outcomes_accessible",
254
+ "input_hash",
255
+ }
256
+ data = _strict_mapping(value, required, label="selector evaluation input")
257
+ if (
258
+ data["schema_version"] != 1
259
+ or data["artifact"] != _INPUT_ARTIFACT
260
+ or data["stage"] != "frozen"
261
+ or data["data_status"] != "VERIFIED_PRE_ACTION_INPUTS"
262
+ or data["is_synthetic"] is not False
263
+ or data["outcomes_included"] is not False
264
+ or data["other_arm_outcomes_accessible"] is not False
265
+ ):
266
+ raise ValidationError("selector evaluation input identity or leakage boundary is invalid")
267
+ design = validate_selector_campaign_design(
268
+ _mapping(data["campaign_design"], label="campaign_design")
269
+ )
270
+ design_payload = _jsonable_mapping(design)
271
+ if content_hash(design_payload) != data["campaign_design_hash"]:
272
+ raise ValidationError("selector evaluation campaign design hash mismatch")
273
+ for name in (
274
+ "campaign_design_hash",
275
+ "representative_manifest_hash",
276
+ "partial_support_plan_hash",
277
+ "input_hash",
278
+ ):
279
+ _require_sha256(data[name], label=name)
280
+ extractor = _strict_mapping(
281
+ data["extractor"],
282
+ {
283
+ "id",
284
+ "revision",
285
+ "sha256",
286
+ "feature_schema_id",
287
+ "feature_schema_sha256",
288
+ },
289
+ label="selector evaluation extractor",
290
+ )
291
+ for name in ("id", "revision", "feature_schema_id"):
292
+ _text(extractor[name], label=f"extractor.{name}")
293
+ for name in ("sha256", "feature_schema_sha256"):
294
+ _require_sha256(extractor[name], label=f"extractor.{name}")
295
+ sources = _validate_sources(data["source_runs"])
296
+ if not sources:
297
+ raise ValidationError("selector evaluation input requires verified source runs")
298
+ groups_raw = _sequence(data["groups"], label="selector evaluation groups")
299
+ groups: list[dict[str, Any]] = []
300
+ seen: set[str] = set()
301
+ common_count = 0
302
+ suites: set[str] = set()
303
+ for index, raw in enumerate(groups_raw):
304
+ group = _strict_mapping(
305
+ raw,
306
+ {
307
+ "group_id",
308
+ "suite_id",
309
+ "common_support",
310
+ "state_hash",
311
+ "observation",
312
+ "eligible_families",
313
+ "model_slots",
314
+ "seed",
315
+ },
316
+ label=f"selector evaluation group {index}",
317
+ )
318
+ group_id = _group_id(group["group_id"], label="selector evaluation group ID")
319
+ if group_id in seen:
320
+ raise ValidationError("selector evaluation group IDs must be unique")
321
+ seen.add(group_id)
322
+ suite_id = _text(group["suite_id"], label="selector evaluation suite")
323
+ if suite_id not in design["suite_ids"]:
324
+ raise ValidationError("selector evaluation group suite is undeclared")
325
+ suites.add(suite_id)
326
+ common_support = _boolean(
327
+ group["common_support"], label="selector evaluation common_support"
328
+ )
329
+ common_count += int(common_support)
330
+ _require_sha256(group["state_hash"], label="selector evaluation state_hash")
331
+ observation = _observation_from_artifact(to_jsonable(group["observation"]))
332
+ if observation.state_hash != group["state_hash"]:
333
+ raise ValidationError("selector evaluation observation is bound to another state")
334
+ if (
335
+ observation.extractor_id != extractor["id"]
336
+ or observation.extractor_revision != extractor["revision"]
337
+ ):
338
+ raise ValidationError("selector evaluation observation changed extractor identity")
339
+ families = _family_sequence(group["eligible_families"])
340
+ if len(families) != (5 if common_support else 3):
341
+ raise ValidationError("selector evaluation eligibility cardinality is invalid")
342
+ model_slots = tuple(
343
+ _text(item, label="selector evaluation model slot")
344
+ for item in _sequence(group["model_slots"], label="selector model slots")
345
+ )
346
+ if model_slots != ("anchor_a", "anchor_b"):
347
+ raise ValidationError("selector evaluation requires exact anchor_a/anchor_b slots")
348
+ if group["seed"] != 17:
349
+ raise ValidationError("selector evaluation requires continuation seed 17")
350
+ groups.append(
351
+ {
352
+ **group,
353
+ "observation": _observation_to_artifact(observation),
354
+ "eligible_families": [family.value for family in families],
355
+ "model_slots": list(model_slots),
356
+ }
357
+ )
358
+ if (
359
+ len(groups) != design["required_independent_groups"]
360
+ or common_count != design["minimum_common_support_groups"]
361
+ or suites != set(design["suite_ids"])
362
+ ):
363
+ raise ValidationError("selector evaluation group support differs from campaign design")
364
+ if (
365
+ content_hash({key: item for key, item in data.items() if key != "input_hash"})
366
+ != data["input_hash"]
367
+ ):
368
+ raise ValidationError("selector evaluation input hash differs from its content")
369
+ return freeze_mapping(
370
+ {
371
+ **data,
372
+ "campaign_design": design_payload,
373
+ "source_runs": sources,
374
+ "groups": tuple(groups),
375
+ }
376
+ )
377
+
378
+
379
+ def freeze_zero_shot_router_replay(
380
+ evaluation_input: Mapping[str, Any],
381
+ decisions: Sequence[Mapping[str, Any]],
382
+ *,
383
+ selector_id: str,
384
+ revision: str,
385
+ router_prompt_sha256: str,
386
+ backend_sha256: str,
387
+ routing_run_sha256: str,
388
+ ) -> Mapping[str, Any]:
389
+ """Freeze externally generated, outcome-blind zero-shot routing choices."""
390
+
391
+ inputs = validate_selector_evaluation_input(evaluation_input)
392
+ for name, digest in (
393
+ ("router prompt", router_prompt_sha256),
394
+ ("backend", backend_sha256),
395
+ ("routing run", routing_run_sha256),
396
+ ):
397
+ _require_sha256(digest, label=f"zero-shot {name}")
398
+ _text(selector_id, label="zero-shot selector ID")
399
+ _text(revision, label="zero-shot selector revision")
400
+ groups = {group["group_id"]: group for group in inputs["groups"]}
401
+ rows: list[dict[str, Any]] = []
402
+ seen: set[str] = set()
403
+ for index, raw in enumerate(decisions):
404
+ row = _strict_mapping(
405
+ raw,
406
+ {"group_id", "selected_family", "selected_release_id"},
407
+ label=f"zero-shot decision {index}",
408
+ )
409
+ group_id = _group_id(row["group_id"], label="zero-shot group ID")
410
+ if group_id in seen or group_id not in groups:
411
+ raise ValidationError("zero-shot decisions have duplicate or unknown groups")
412
+ seen.add(group_id)
413
+ family = _family(row["selected_family"])
414
+ release_id = _text(row["selected_release_id"], label="zero-shot release")
415
+ _validate_selected_action(group=groups[group_id], family=family, release_id=release_id)
416
+ rows.append(
417
+ {
418
+ "group_id": group_id,
419
+ "selected_family": family.value,
420
+ "selected_release_id": release_id,
421
+ }
422
+ )
423
+ if seen != set(groups):
424
+ raise ValidationError("zero-shot replay must contain one choice for every input group")
425
+ base = {
426
+ "schema_version": 1,
427
+ "artifact": _ZERO_SHOT_ARTIFACT,
428
+ "stage": "frozen",
429
+ "selector_kind": "zero_shot_router",
430
+ "selector_id": selector_id,
431
+ "revision": revision,
432
+ "evaluation_input_hash": inputs["input_hash"],
433
+ "attestation": {
434
+ "router_prompt_sha256": router_prompt_sha256,
435
+ "backend_sha256": backend_sha256,
436
+ "routing_run_sha256": routing_run_sha256,
437
+ "outcomes_accessed": False,
438
+ "other_arm_outcomes_accessed": False,
439
+ },
440
+ "decisions": sorted(rows, key=lambda item: item["group_id"]),
441
+ }
442
+ return freeze_mapping({**base, "bundle_hash": content_hash(base)})
443
+
444
+
445
+ def validate_zero_shot_router_replay(
446
+ evaluation_input: Mapping[str, Any],
447
+ value: Mapping[str, Any],
448
+ ) -> Mapping[str, Any]:
449
+ inputs = validate_selector_evaluation_input(evaluation_input)
450
+ required = {
451
+ "schema_version",
452
+ "artifact",
453
+ "stage",
454
+ "selector_kind",
455
+ "selector_id",
456
+ "revision",
457
+ "evaluation_input_hash",
458
+ "attestation",
459
+ "decisions",
460
+ "bundle_hash",
461
+ }
462
+ data = _strict_mapping(value, required, label="zero-shot replay")
463
+ if (
464
+ data["schema_version"] != 1
465
+ or data["artifact"] != _ZERO_SHOT_ARTIFACT
466
+ or data["stage"] != "frozen"
467
+ or data["selector_kind"] != "zero_shot_router"
468
+ or data["evaluation_input_hash"] != inputs["input_hash"]
469
+ ):
470
+ raise ValidationError("zero-shot replay identity differs from its evaluation input")
471
+ _text(data["selector_id"], label="zero-shot selector ID")
472
+ _text(data["revision"], label="zero-shot selector revision")
473
+ attestation = _strict_mapping(
474
+ data["attestation"],
475
+ {
476
+ "router_prompt_sha256",
477
+ "backend_sha256",
478
+ "routing_run_sha256",
479
+ "outcomes_accessed",
480
+ "other_arm_outcomes_accessed",
481
+ },
482
+ label="zero-shot attestation",
483
+ )
484
+ for name in ("router_prompt_sha256", "backend_sha256", "routing_run_sha256"):
485
+ _require_sha256(attestation[name], label=f"zero-shot {name}")
486
+ if (
487
+ attestation["outcomes_accessed"] is not False
488
+ or attestation["other_arm_outcomes_accessed"] is not False
489
+ ):
490
+ raise ValidationError("zero-shot replay may not access test outcomes")
491
+ rebuilt = freeze_zero_shot_router_replay(
492
+ inputs,
493
+ _sequence(data["decisions"], label="zero-shot decisions"),
494
+ selector_id=data["selector_id"],
495
+ revision=data["revision"],
496
+ router_prompt_sha256=attestation["router_prompt_sha256"],
497
+ backend_sha256=attestation["backend_sha256"],
498
+ routing_run_sha256=attestation["routing_run_sha256"],
499
+ )
500
+ if rebuilt["bundle_hash"] != data["bundle_hash"]:
501
+ raise ValidationError("zero-shot replay hash differs from its content")
502
+ return freeze_mapping(data)
503
+
504
+
505
+ def build_selector_campaign_decisions(
506
+ campaign: Mapping[str, Any],
507
+ evaluation_input: Mapping[str, Any],
508
+ policy_artifacts: Mapping[str, Mapping[str, Any]],
509
+ ) -> Mapping[str, Any]:
510
+ """Run 13 outcome-blind policies; the diagnostic oracle is deliberately absent."""
511
+
512
+ manifest = validate_selector_campaign_manifest(campaign)
513
+ inputs = validate_selector_evaluation_input(evaluation_input)
514
+ _validate_campaign_input_binding(manifest, inputs)
515
+ if set(policy_artifacts) != _POLICY_ARTIFACT_IDS:
516
+ raise ValidationError(
517
+ "selector decision builder requires exact artifacts for five frozen policies"
518
+ )
519
+ policies = {policy["policy_id"]: policy for policy in manifest["policies"]}
520
+ rank_one = _campaign_rank_one_releases(manifest)
521
+ loaded: dict[str, Any] = {}
522
+ artifact_hashes: dict[str, str] = {}
523
+ for policy_id in sorted(_POLICY_ARTIFACT_IDS):
524
+ policy = policies[policy_id]
525
+ binding = policy["parameters"]["artifact_binding"]
526
+ artifact = _mapping(
527
+ policy_artifacts[policy_id],
528
+ label=f"selector artifact {policy_id}",
529
+ )
530
+ if binding["selector_kind"] == "zero_shot_router":
531
+ replay = validate_zero_shot_router_replay(inputs, artifact)
532
+ if (
533
+ replay["selector_id"] != binding["selector_id"]
534
+ or replay["revision"] != binding["revision"]
535
+ or replay["bundle_hash"] != binding["sha256"]
536
+ ):
537
+ raise ValidationError("zero-shot replay differs from its campaign binding")
538
+ loaded[policy_id] = {row["group_id"]: row for row in replay["decisions"]}
539
+ artifact_hashes[policy_id] = replay["bundle_hash"]
540
+ continue
541
+ bundle = SelectorArtifactBundle.from_artifact(artifact)
542
+ selector_kind = bundle.model_payload.get("selector_kind")
543
+ if (
544
+ bundle.stage is not SelectorArtifactStage.FROZEN
545
+ or selector_kind != binding["selector_kind"]
546
+ or selector_kind not in _STANDARD_SELECTOR_KINDS
547
+ or bundle.selector_card_id != binding["selector_id"]
548
+ or bundle.revision != binding["revision"]
549
+ or bundle.bundle_hash != binding["sha256"]
550
+ ):
551
+ raise ValidationError(f"selector artifact {policy_id} differs from its binding")
552
+ input_extractor = inputs["extractor"]
553
+ if (
554
+ bundle.extractor_id != input_extractor["id"]
555
+ or bundle.extractor_revision != input_extractor["revision"]
556
+ or bundle.extractor_hash != input_extractor["sha256"]
557
+ or bundle.feature_schema_id != input_extractor["feature_schema_id"]
558
+ or bundle.feature_schema_hash != input_extractor["feature_schema_sha256"]
559
+ or dict(bundle.representative_by_family) != rank_one
560
+ ):
561
+ raise ValidationError(
562
+ f"selector artifact {policy_id} changed features or representatives"
563
+ )
564
+ loaded[policy_id] = bundle.selector()
565
+ artifact_hashes[policy_id] = bundle.bundle_hash
566
+
567
+ rows: list[dict[str, Any]] = []
568
+ oracle_policy_ids = {
569
+ policy_id
570
+ for policy_id, policy in policies.items()
571
+ if policy["kind"] == "observed_action_oracle"
572
+ }
573
+ if oracle_policy_ids != {"observed_action_oracle"}:
574
+ raise ValidationError("selector campaign diagnostic oracle contract is invalid")
575
+ for group in inputs["groups"]:
576
+ group_id = group["group_id"]
577
+ eligible = tuple(SemanticFamily(item) for item in group["eligible_families"])
578
+ observation = _observation_from_artifact(to_jsonable(group["observation"]))
579
+ state = FrozenAgentState(
580
+ query="[outcome-blind selector feature evaluation]",
581
+ task_id=group_id,
582
+ )
583
+ for policy_id, policy in policies.items():
584
+ if policy_id in oracle_policy_ids:
585
+ continue
586
+ family, release_id = _select_policy_action(
587
+ policy,
588
+ group=group,
589
+ state=state,
590
+ features=observation.features,
591
+ eligible=eligible,
592
+ rank_one=rank_one,
593
+ loaded=loaded,
594
+ random_seed=manifest["metadata"]["random_seed"],
595
+ )
596
+ expected_release = _release_for(family, rank_one)[1]
597
+ if release_id != expected_release:
598
+ raise ValidationError(
599
+ f"selector policy {policy_id} changed the frozen representative release"
600
+ )
601
+ rows.append(
602
+ {
603
+ "group_id": group_id,
604
+ "policy_id": policy_id,
605
+ "selected_family": family.value,
606
+ "selected_release_id": release_id,
607
+ "selection_eligible": True,
608
+ "diagnostic_only": policy["diagnostic_only"],
609
+ }
610
+ )
611
+ base = {
612
+ "schema_version": 1,
613
+ "artifact": _DECISION_ARTIFACT,
614
+ "stage": "frozen",
615
+ "data_status": "OUTCOME_BLIND_DECISIONS",
616
+ "is_synthetic": False,
617
+ "campaign_manifest_hash": manifest["campaign_hash"],
618
+ "evaluation_input_hash": inputs["input_hash"],
619
+ "policy_artifact_hashes": artifact_hashes,
620
+ "records": sorted(rows, key=lambda row: (row["group_id"], row["policy_id"])),
621
+ "outcomes_accessed": False,
622
+ "other_arm_outcomes_accessed": False,
623
+ "diagnostic_oracle_included": False,
624
+ }
625
+ artifact = freeze_mapping({**base, "decision_hash": content_hash(base)})
626
+ return validate_selector_campaign_decisions(manifest, inputs, artifact)
627
+
628
+
629
+ def validate_selector_campaign_decisions(
630
+ campaign: Mapping[str, Any],
631
+ evaluation_input: Mapping[str, Any],
632
+ decisions: Mapping[str, Any],
633
+ ) -> Mapping[str, Any]:
634
+ manifest = validate_selector_campaign_manifest(campaign)
635
+ inputs = validate_selector_evaluation_input(evaluation_input)
636
+ _validate_campaign_input_binding(manifest, inputs)
637
+ required = {
638
+ "schema_version",
639
+ "artifact",
640
+ "stage",
641
+ "data_status",
642
+ "is_synthetic",
643
+ "campaign_manifest_hash",
644
+ "evaluation_input_hash",
645
+ "policy_artifact_hashes",
646
+ "records",
647
+ "outcomes_accessed",
648
+ "other_arm_outcomes_accessed",
649
+ "diagnostic_oracle_included",
650
+ "decision_hash",
651
+ }
652
+ data = _strict_mapping(decisions, required, label="selector campaign decisions")
653
+ if (
654
+ data["schema_version"] != 1
655
+ or data["artifact"] != _DECISION_ARTIFACT
656
+ or data["stage"] != "frozen"
657
+ or data["data_status"] != "OUTCOME_BLIND_DECISIONS"
658
+ or data["is_synthetic"] is not False
659
+ or data["campaign_manifest_hash"] != manifest["campaign_hash"]
660
+ or data["evaluation_input_hash"] != inputs["input_hash"]
661
+ or data["outcomes_accessed"] is not False
662
+ or data["other_arm_outcomes_accessed"] is not False
663
+ or data["diagnostic_oracle_included"] is not False
664
+ ):
665
+ raise ValidationError("selector decision artifact violates the outcome-blind boundary")
666
+ hashes = _mapping(data["policy_artifact_hashes"], label="selector policy hashes")
667
+ if set(hashes) != _POLICY_ARTIFACT_IDS:
668
+ raise ValidationError("selector decision artifact policy hashes are incomplete")
669
+ for digest in hashes.values():
670
+ _require_sha256(digest, label="selector policy artifact hash")
671
+ policies = {
672
+ policy["policy_id"]: policy
673
+ for policy in manifest["policies"]
674
+ if policy["kind"] != "observed_action_oracle"
675
+ }
676
+ groups = {group["group_id"]: group for group in inputs["groups"]}
677
+ rank_one = _campaign_rank_one_releases(manifest)
678
+ seen: set[tuple[str, str]] = set()
679
+ parsed_rows: list[dict[str, Any]] = []
680
+ for index, raw in enumerate(_sequence(data["records"], label="selector decisions")):
681
+ row = _strict_mapping(
682
+ raw,
683
+ {
684
+ "group_id",
685
+ "policy_id",
686
+ "selected_family",
687
+ "selected_release_id",
688
+ "selection_eligible",
689
+ "diagnostic_only",
690
+ },
691
+ label=f"selector decision {index}",
692
+ )
693
+ group_id = _group_id(row["group_id"], label="selector decision group ID")
694
+ policy_id = _text(row["policy_id"], label="selector decision policy ID")
695
+ if group_id not in groups or policy_id not in policies:
696
+ raise ValidationError("selector decision references an unknown group or policy")
697
+ key = (group_id, policy_id)
698
+ if key in seen:
699
+ raise ValidationError("selector decisions contain duplicate group-policy rows")
700
+ seen.add(key)
701
+ family = _family(row["selected_family"])
702
+ release_id = _text(row["selected_release_id"], label="selector decision release")
703
+ _validate_selected_action(
704
+ group=groups[group_id],
705
+ family=family,
706
+ release_id=release_id,
707
+ )
708
+ if release_id != _release_for(family, rank_one)[1]:
709
+ raise ValidationError(
710
+ "selector decision release differs from the campaign representative"
711
+ )
712
+ if (
713
+ row["selection_eligible"] is not True
714
+ or row["diagnostic_only"] is not policies[policy_id]["diagnostic_only"]
715
+ ):
716
+ raise ValidationError("selector decision eligibility or policy role is invalid")
717
+ parsed_rows.append(
718
+ {
719
+ **row,
720
+ "selected_family": family.value,
721
+ }
722
+ )
723
+ expected = {(group_id, policy_id) for group_id in groups for policy_id in policies}
724
+ if seen != expected:
725
+ raise ValidationError("selector decision artifact lacks the complete non-oracle grid")
726
+ _require_sha256(data["decision_hash"], label="selector decision hash")
727
+ if (
728
+ content_hash({key: item for key, item in data.items() if key != "decision_hash"})
729
+ != data["decision_hash"]
730
+ ):
731
+ raise ValidationError("selector decision hash differs from its content")
732
+ return freeze_mapping({**data, "records": tuple(parsed_rows)})
733
+
734
+
735
+ def assemble_selector_campaign_results(
736
+ campaign: Mapping[str, Any],
737
+ evaluation_input: Mapping[str, Any],
738
+ decisions: Mapping[str, Any],
739
+ run_dirs: Sequence[str | Path],
740
+ *,
741
+ extractor: VisibleStateFeatureExtractor,
742
+ utility_definition: UtilityDefinition,
743
+ ) -> Mapping[str, Any]:
744
+ """Join frozen choices to verified outcomes and add the diagnostic oracle."""
745
+
746
+ manifest = validate_selector_campaign_manifest(campaign)
747
+ inputs = validate_selector_evaluation_input(evaluation_input)
748
+ frozen_decisions = validate_selector_campaign_decisions(manifest, inputs, decisions)
749
+ if utility_definition.utility_metric != manifest["primary_metric"]:
750
+ raise ValidationError("selector result utility differs from the campaign primary metric")
751
+ input_extractor = inputs["extractor"]
752
+ if (
753
+ extractor.extractor_id != input_extractor["id"]
754
+ or extractor.extractor_revision != input_extractor["revision"]
755
+ or extractor.extractor_hash != input_extractor["sha256"]
756
+ or extractor.feature_schema_id != input_extractor["feature_schema_id"]
757
+ or extractor.feature_schema_hash != input_extractor["feature_schema_sha256"]
758
+ ):
759
+ raise ValidationError("selector result assembler changed the frozen feature extractor")
760
+
761
+ rank_one = _campaign_rank_one_releases(manifest)
762
+ outcomes, sources = _verified_group_outcomes(
763
+ run_dirs,
764
+ inputs=inputs,
765
+ extractor=extractor,
766
+ utility_definition=utility_definition,
767
+ rank_one=rank_one,
768
+ )
769
+ if sources != tuple(inputs["source_runs"]):
770
+ raise ValidationError("selector result source runs differ from pre-action input")
771
+ groups = {group["group_id"]: group for group in inputs["groups"]}
772
+ result_rows: list[dict[str, Any]] = []
773
+ for decision in frozen_decisions["records"]:
774
+ group = groups[decision["group_id"]]
775
+ family = SemanticFamily(decision["selected_family"])
776
+ selected = outcomes[decision["group_id"]][family]
777
+ baseline = outcomes[decision["group_id"]][SemanticFamily.NO_OP]
778
+ intervened = family is not SemanticFamily.NO_OP
779
+ result_rows.append(
780
+ _campaign_result_row(
781
+ group=group,
782
+ policy_id=decision["policy_id"],
783
+ family=family,
784
+ release_id=decision["selected_release_id"],
785
+ value=selected["value"],
786
+ no_op_value=baseline["value"],
787
+ cost=selected["cost"],
788
+ diagnostic_only=decision["diagnostic_only"],
789
+ hard_harm=intervened and bool(selected["hard_harm"]),
790
+ )
791
+ )
792
+ oracle_policy = next(
793
+ policy for policy in manifest["policies"] if policy["kind"] == "observed_action_oracle"
794
+ )
795
+ for group_id, group in groups.items():
796
+ available = outcomes[group_id]
797
+ selected_family = min(
798
+ available,
799
+ key=lambda family: (
800
+ -available[family]["value"],
801
+ 0 if family is SemanticFamily.NO_OP else 1,
802
+ family.value,
803
+ ),
804
+ )
805
+ selected = available[selected_family]
806
+ baseline = available[SemanticFamily.NO_OP]
807
+ result_rows.append(
808
+ _campaign_result_row(
809
+ group=group,
810
+ policy_id=oracle_policy["policy_id"],
811
+ family=selected_family,
812
+ release_id=selected["release_id"],
813
+ value=selected["value"],
814
+ no_op_value=baseline["value"],
815
+ cost=selected["cost"],
816
+ diagnostic_only=True,
817
+ hard_harm=(
818
+ selected_family is not SemanticFamily.NO_OP and bool(selected["hard_harm"])
819
+ ),
820
+ )
821
+ )
822
+ result = {
823
+ "schema_version": 1,
824
+ "artifact": "agentinputlab.selector_campaign_results",
825
+ "stage": "verified",
826
+ "data_status": "VERIFIED_REAL_RESULTS",
827
+ "is_synthetic": False,
828
+ "campaign_manifest_hash": manifest["campaign_hash"],
829
+ "source_runs": list(sources),
830
+ "records": result_rows,
831
+ }
832
+ return validate_selector_campaign_results(manifest, result)
833
+
834
+
835
+ def _verified_group_outcomes(
836
+ run_dirs: Sequence[str | Path],
837
+ *,
838
+ inputs: Mapping[str, Any],
839
+ extractor: VisibleStateFeatureExtractor,
840
+ utility_definition: UtilityDefinition,
841
+ rank_one: Mapping[SemanticFamily, str],
842
+ ) -> tuple[dict[str, dict[SemanticFamily, dict[str, Any]]], tuple[Mapping[str, Any], ...]]:
843
+ expected_groups = {group["group_id"]: group for group in inputs["groups"]}
844
+ cell_outcomes: dict[str, dict[SemanticFamily, list[EvaluatedArmOutcome]]] = defaultdict(
845
+ lambda: defaultdict(list)
846
+ )
847
+ sources: list[dict[str, str]] = []
848
+ for root_value in run_dirs:
849
+ root = Path(root_value).expanduser().resolve()
850
+ manifest, records, source = _verified_source(root)
851
+ common_support = _validate_evaluation_manifest(
852
+ manifest,
853
+ representative_manifest_hash=inputs["representative_manifest_hash"],
854
+ rank_one=rank_one,
855
+ expected_partial_plan_hash=inputs["partial_support_plan_hash"],
856
+ )
857
+ sources.append(source)
858
+ arm_by_id = {arm.arm_id: arm for arm in manifest.arms}
859
+ no_op_ids = [arm.arm_id for arm in manifest.arms if arm.family is SemanticFamily.NO_OP]
860
+ if len(no_op_ids) != 1:
861
+ raise ValidationError("selector source manifest requires one NO_OP arm")
862
+ representative_arm_ids = {
863
+ arm.family: arm.arm_id
864
+ for arm in manifest.arms
865
+ if arm.family is not SemanticFamily.NO_OP
866
+ }
867
+ for record in records:
868
+ converted, observation, _profile, _model, _backend = _convert_result_record(
869
+ record,
870
+ arm_by_id=arm_by_id,
871
+ no_op_arm_id=no_op_ids[0],
872
+ representative_arm_ids=representative_arm_ids,
873
+ representative_by_family=rank_one,
874
+ extractor=extractor,
875
+ selector_split_override="test",
876
+ )
877
+ group_id = converted[0].group_id
878
+ group = expected_groups.get(group_id)
879
+ if group is None:
880
+ raise ValidationError("selector outcomes contain an unknown group")
881
+ if (
882
+ observation.feature_hash != group["observation"]["feature_hash"]
883
+ or common_support is not group["common_support"]
884
+ ):
885
+ raise ValidationError("selector outcomes differ from the frozen pre-action input")
886
+ for outcome in converted:
887
+ cell_outcomes[group_id][outcome.family].append(outcome)
888
+ if set(cell_outcomes) != set(expected_groups):
889
+ raise ValidationError("selector outcomes do not cover every frozen group")
890
+ aggregated: dict[str, dict[SemanticFamily, dict[str, Any]]] = {}
891
+ for group_id, by_family in cell_outcomes.items():
892
+ eligible = {
893
+ SemanticFamily(value) for value in expected_groups[group_id]["eligible_families"]
894
+ }
895
+ expected_actions = {SemanticFamily.NO_OP, *eligible}
896
+ if set(by_family) != expected_actions:
897
+ raise ValidationError("selector outcomes differ from the frozen group eligibility mask")
898
+ aggregated[group_id] = {}
899
+ for family, rows in by_family.items():
900
+ if len(rows) != 2:
901
+ raise ValidationError(
902
+ "selector outcome action requires exactly two model-slot observations"
903
+ )
904
+ values = [utility_definition.value(row) for row in rows]
905
+ release_ids = {row.release_id for row in rows}
906
+ if len(release_ids) != 1:
907
+ raise ValidationError("selector outcome action changed release identity")
908
+ aggregated[group_id][family] = {
909
+ "value": fmean(values),
910
+ "cost": fmean(row.cost for row in rows),
911
+ "hard_harm": any(row.risk > 0.0 for row in rows),
912
+ "release_id": next(iter(release_ids)),
913
+ }
914
+ return aggregated, tuple(sorted(sources, key=lambda item: item["run_identity_hash"]))
915
+
916
+
917
+ def _pre_action_record(
918
+ record: Mapping[str, Any],
919
+ *,
920
+ arm_by_id: Mapping[str, Any],
921
+ extractor: VisibleStateFeatureExtractor,
922
+ common_support: bool,
923
+ ) -> dict[str, Any]:
924
+ cell = _mapping(record.get("cell"), label="selector source cell")
925
+ result = _mapping(record.get("result"), label="selector source result")
926
+ if result.get("status") != "succeeded":
927
+ raise ArtifactError("selector evaluation requires succeeded source cells")
928
+ payload = _mapping(result.get("payload"), label="selector source payload")
929
+ selector_record = _strict_mapping(
930
+ payload.get("selector_record"),
931
+ {"schema_version", "state", "group_id", "eligible_families"},
932
+ label="selector source pre-action record",
933
+ )
934
+ if selector_record["schema_version"] != 1:
935
+ raise ArtifactError("unsupported selector pre-action record schema")
936
+ state = _state_from_artifact(selector_record["state"])
937
+ observation = extractor.extract(state)
938
+ _validate_pre_action_receipt(
939
+ payload.get("pre_action"),
940
+ observation=observation,
941
+ extractor=extractor,
942
+ )
943
+ cell_arm_ids = tuple(
944
+ _text(item, label="selector source arm ID")
945
+ for item in _sequence(cell.get("arm_ids"), label="selector source arm IDs")
946
+ )
947
+ if set(cell_arm_ids) != set(arm_by_id):
948
+ raise ValidationError("selector source cell arms differ from its manifest")
949
+ observed_families = {
950
+ arm_by_id[arm_id].family
951
+ for arm_id in cell_arm_ids
952
+ if arm_by_id[arm_id].family is not SemanticFamily.NO_OP
953
+ }
954
+ eligible = set(_family_sequence(selector_record["eligible_families"]))
955
+ if eligible != observed_families:
956
+ raise ValidationError("selector pre-action eligibility differs from the executed arm block")
957
+ if len(eligible) != (5 if common_support else 3):
958
+ raise ValidationError("selector source support cardinality is invalid")
959
+ benchmark_id = _text(cell.get("benchmark_id"), label="selector source benchmark")
960
+ raw_group_id = _text(selector_record["group_id"], label="selector source group ID")
961
+ group_id = "group_" + content_hash({"benchmark_id": benchmark_id, "group_id": raw_group_id})
962
+ return {
963
+ "group_id": group_id,
964
+ "suite_id": benchmark_id,
965
+ "state_hash": state.state_hash,
966
+ "observation": _observation_to_artifact(observation),
967
+ "eligible_families": [family.value for family in _CORE_FAMILIES if family in eligible],
968
+ "model_slot": _text(cell.get("model_slot"), label="selector source model slot"),
969
+ "seed": _integer(cell.get("seed"), label="selector source seed"),
970
+ }
971
+
972
+
973
+ def _verified_source(
974
+ root: Path,
975
+ ) -> tuple[ExperimentManifest, tuple[Mapping[str, Any], ...], dict[str, str]]:
976
+ verification = verify_experiment_run_directory(root)
977
+ if not verification["complete"] or not verification["all_succeeded"]:
978
+ raise ArtifactError("selector evaluation requires complete, all-succeeded verified runs")
979
+ manifest = load_experiment_manifest(root / "manifest.json")
980
+ records = load_verified_cell_results(root)
981
+ source = {
982
+ "study_id": manifest.study_id,
983
+ "run_identity_hash": _sha256(verification["identity_hash"], label="source run identity"),
984
+ "manifest_hash": _sha256(verification["manifest_hash"], label="source manifest hash"),
985
+ "plan_hash": _sha256(verification["plan_hash"], label="source plan hash"),
986
+ "result_set_hash": content_hash(records),
987
+ }
988
+ return manifest, records, source
989
+
990
+
991
+ def _validate_evaluation_manifest(
992
+ manifest: ExperimentManifest,
993
+ *,
994
+ representative_manifest_hash: str,
995
+ rank_one: Mapping[SemanticFamily, str],
996
+ expected_partial_plan_hash: str,
997
+ ) -> bool:
998
+ common_support = manifest.study_id == "primary.common_support"
999
+ partial_support = manifest.study_id.startswith("selector.partial_support.")
1000
+ if not common_support and not partial_support:
1001
+ raise ValidationError("selector evaluation received an undeclared source study")
1002
+ if common_support:
1003
+ census = manifest.metadata.get("experiment_census", {})
1004
+ if (
1005
+ manifest.stage is not ExperimentStage.CONFIRMATORY
1006
+ or census.get("campaign_kind") != "common_support"
1007
+ or census.get("independent_group_target") != 526
1008
+ or census.get("expected_agent_episodes") != 6312
1009
+ ):
1010
+ raise ValidationError("selector common-support source is not confirmatory")
1011
+ expected_count = 5
1012
+ else:
1013
+ census = manifest.metadata.get("experiment_census", {})
1014
+ if (
1015
+ manifest.stage is not ExperimentStage.SELECTOR_CONFIRMATION
1016
+ or manifest.metadata.get("partial_support") is not True
1017
+ or manifest.metadata.get("common_support") is not False
1018
+ or manifest.metadata.get("fit_or_calibrate_allowed") is not False
1019
+ or manifest.metadata.get("test_outcomes_visible_to_selector") is not False
1020
+ or manifest.metadata.get("eligibility_plan_hash") != expected_partial_plan_hash
1021
+ or census.get("campaign_kind") != "selector_partial_support"
1022
+ or census.get("independent_group_target") != 10
1023
+ or census.get("expected_agent_episodes") != 80
1024
+ ):
1025
+ raise ValidationError("selector partial-support source is not frozen")
1026
+ expected_count = 3
1027
+ if (
1028
+ manifest.metadata.get("representative_manifest_hash") != representative_manifest_hash
1029
+ or manifest.model_slots != ("anchor_a", "anchor_b")
1030
+ or manifest.seeds != (17,)
1031
+ ):
1032
+ raise ValidationError("selector source changed representatives, models, or seed")
1033
+ no_ops = [arm for arm in manifest.arms if arm.family is SemanticFamily.NO_OP]
1034
+ interventions = [arm for arm in manifest.arms if arm.family is not SemanticFamily.NO_OP]
1035
+ if (
1036
+ len(no_ops) != 1
1037
+ or no_ops[0].release_id != "baseline.no_op"
1038
+ or len(interventions) != expected_count
1039
+ or len({arm.family for arm in interventions}) != expected_count
1040
+ ):
1041
+ raise ValidationError("selector source arm support is invalid")
1042
+ for arm in interventions:
1043
+ if (
1044
+ arm.release_id != rank_one.get(arm.family)
1045
+ or arm.parameters.get("representative_rank") != 1
1046
+ or arm.parameters.get("representative_manifest_hash") != representative_manifest_hash
1047
+ ):
1048
+ raise ValidationError("selector source arm is not a rank-1 representative")
1049
+ return common_support
1050
+
1051
+
1052
+ def _select_policy_action(
1053
+ policy: Mapping[str, Any],
1054
+ *,
1055
+ group: Mapping[str, Any],
1056
+ state: FrozenAgentState,
1057
+ features: Any,
1058
+ eligible: Sequence[SemanticFamily],
1059
+ rank_one: Mapping[SemanticFamily, str],
1060
+ loaded: Mapping[str, Any],
1061
+ random_seed: int,
1062
+ ) -> tuple[SemanticFamily, str]:
1063
+ kind = policy["kind"]
1064
+ policy_id = policy["policy_id"]
1065
+ parameters = policy["parameters"]
1066
+ if kind == "no_op":
1067
+ return SemanticFamily.NO_OP, "baseline.no_op"
1068
+ if kind == "random_eligible":
1069
+ actions = (SemanticFamily.NO_OP, *eligible)
1070
+ seed = int(
1071
+ content_hash(
1072
+ {
1073
+ "campaign_seed": random_seed,
1074
+ "group_id": group["group_id"],
1075
+ "policy_id": policy_id,
1076
+ }
1077
+ ),
1078
+ 16,
1079
+ )
1080
+ family = random.Random(seed).choice(actions)
1081
+ return _release_for(family, rank_one)
1082
+ if kind == "fixed_family":
1083
+ family = SemanticFamily(parameters["family"])
1084
+ return (
1085
+ _release_for(family, rank_one)
1086
+ if family in eligible
1087
+ else (SemanticFamily.NO_OP, "baseline.no_op")
1088
+ )
1089
+ if kind == "best_fixed_global":
1090
+ family = SemanticFamily(parameters["selected_family"])
1091
+ return (
1092
+ _release_for(family, rank_one)
1093
+ if family in eligible
1094
+ else (SemanticFamily.NO_OP, "baseline.no_op")
1095
+ )
1096
+ if kind != "frozen_artifact_policy":
1097
+ raise ValidationError(f"unsupported outcome-blind selector policy kind: {kind}")
1098
+ artifact = loaded[policy_id]
1099
+ if isinstance(artifact, Mapping):
1100
+ replay = artifact[group["group_id"]]
1101
+ return SemanticFamily(replay["selected_family"]), replay["selected_release_id"]
1102
+ decision = artifact.select(
1103
+ state,
1104
+ features,
1105
+ eligible_families=eligible,
1106
+ )
1107
+ if decision.family is SemanticFamily.NO_OP:
1108
+ return SemanticFamily.NO_OP, "baseline.no_op"
1109
+ if decision.family not in eligible:
1110
+ raise ValidationError(f"frozen selector {policy_id} selected an ineligible family")
1111
+ expected_release = rank_one[decision.family]
1112
+ if decision.release_id not in (None, expected_release):
1113
+ raise ValidationError(f"frozen selector {policy_id} changed representative release")
1114
+ return decision.family, expected_release
1115
+
1116
+
1117
+ def _campaign_result_row(
1118
+ *,
1119
+ group: Mapping[str, Any],
1120
+ policy_id: str,
1121
+ family: SemanticFamily,
1122
+ release_id: str,
1123
+ value: float,
1124
+ no_op_value: float,
1125
+ cost: float,
1126
+ diagnostic_only: bool,
1127
+ hard_harm: bool,
1128
+ ) -> dict[str, Any]:
1129
+ return {
1130
+ "group_id": group["group_id"],
1131
+ "suite_id": group["suite_id"],
1132
+ "policy_id": policy_id,
1133
+ "selected_family": family.value,
1134
+ "selected_release_id": release_id,
1135
+ "value": value,
1136
+ "no_op_value": no_op_value,
1137
+ "hard_harm": hard_harm,
1138
+ "cost": cost,
1139
+ "intervened": family is not SemanticFamily.NO_OP,
1140
+ "selection_eligible": True,
1141
+ "common_support": group["common_support"],
1142
+ "diagnostic_only": diagnostic_only,
1143
+ }
1144
+
1145
+
1146
+ def _validate_campaign_input_binding(
1147
+ campaign: Mapping[str, Any],
1148
+ inputs: Mapping[str, Any],
1149
+ ) -> None:
1150
+ design = inputs["campaign_design"]
1151
+ if (
1152
+ campaign["campaign_id"] != design["campaign_id"]
1153
+ or campaign["primary_metric"] != design["primary_metric"]
1154
+ or campaign["evaluation_split"] != design["evaluation_split"]
1155
+ or campaign["representative_manifest_hash"] != inputs["representative_manifest_hash"]
1156
+ or campaign["metadata"]["suite_ids"] != design["suite_ids"]
1157
+ or campaign["metadata"]["required_independent_groups"]
1158
+ != design["required_independent_groups"]
1159
+ or campaign["metadata"]["minimum_common_support_groups"]
1160
+ != design["minimum_common_support_groups"]
1161
+ or campaign["metadata"]["random_seed"] != design["random_seed"]
1162
+ ):
1163
+ raise ValidationError("selector campaign differs from its pre-action input design")
1164
+
1165
+
1166
+ def _campaign_rank_one_releases(
1167
+ campaign: Mapping[str, Any],
1168
+ ) -> dict[SemanticFamily, str]:
1169
+ policies = {policy["policy_id"]: policy for policy in campaign["policies"]}
1170
+ releases = {
1171
+ family: policies[f"fixed_{family.value}"]["parameters"]["release_id"]
1172
+ for family in _CORE_FAMILIES
1173
+ }
1174
+ if any(not isinstance(value, str) or not value for value in releases.values()):
1175
+ raise ValidationError("selector campaign fixed-family releases are invalid")
1176
+ return releases
1177
+
1178
+
1179
+ def _rank_one_releases(
1180
+ representatives: Mapping[str, Any],
1181
+ ) -> dict[SemanticFamily, str]:
1182
+ result = {
1183
+ SemanticFamily(row["family"]): row["release_id"]
1184
+ for row in representatives["selections"]
1185
+ if row["rank"] == 1
1186
+ }
1187
+ if set(result) != set(_CORE_FAMILIES):
1188
+ raise ValidationError("selector evaluation requires one rank-1 release per family")
1189
+ return result
1190
+
1191
+
1192
+ def _validate_selected_action(
1193
+ *,
1194
+ group: Mapping[str, Any],
1195
+ family: SemanticFamily,
1196
+ release_id: str,
1197
+ ) -> None:
1198
+ if family is SemanticFamily.NO_OP:
1199
+ if release_id != "baseline.no_op":
1200
+ raise ValidationError("selector NO_OP decision requires baseline.no_op")
1201
+ return
1202
+ if family.value not in group["eligible_families"]:
1203
+ raise ValidationError("selector decision selected a structurally ineligible family")
1204
+ if release_id == "baseline.no_op":
1205
+ raise ValidationError("selector intervention decision requires an intervention release")
1206
+
1207
+
1208
+ def _release_for(
1209
+ family: SemanticFamily,
1210
+ rank_one: Mapping[SemanticFamily, str],
1211
+ ) -> tuple[SemanticFamily, str]:
1212
+ if family is SemanticFamily.NO_OP:
1213
+ return family, "baseline.no_op"
1214
+ return family, rank_one[family]
1215
+
1216
+
1217
+ def _validate_sources(value: Any) -> tuple[dict[str, str], ...]:
1218
+ rows = _sequence(value, label="selector evaluation source runs")
1219
+ parsed: list[dict[str, str]] = []
1220
+ identities: set[str] = set()
1221
+ for index, raw in enumerate(rows):
1222
+ row = _strict_mapping(
1223
+ raw,
1224
+ {
1225
+ "study_id",
1226
+ "run_identity_hash",
1227
+ "manifest_hash",
1228
+ "plan_hash",
1229
+ "result_set_hash",
1230
+ },
1231
+ label=f"selector evaluation source {index}",
1232
+ )
1233
+ parsed_row = {
1234
+ "study_id": _text(row["study_id"], label="selector source study"),
1235
+ "run_identity_hash": _sha256(
1236
+ row["run_identity_hash"], label="selector source identity"
1237
+ ),
1238
+ "manifest_hash": _sha256(row["manifest_hash"], label="selector source manifest"),
1239
+ "plan_hash": _sha256(row["plan_hash"], label="selector source plan"),
1240
+ "result_set_hash": _sha256(row["result_set_hash"], label="selector source results"),
1241
+ }
1242
+ if parsed_row["run_identity_hash"] in identities:
1243
+ raise ValidationError("selector source run identities must be unique")
1244
+ identities.add(parsed_row["run_identity_hash"])
1245
+ parsed.append(parsed_row)
1246
+ return tuple(sorted(parsed, key=lambda item: item["run_identity_hash"]))
1247
+
1248
+
1249
+ def _jsonable_mapping(value: Mapping[str, Any]) -> dict[str, Any]:
1250
+ try:
1251
+ normalized = to_jsonable(value)
1252
+ except (TypeError, ValueError) as exc:
1253
+ raise ValidationError("selector artifact mapping must be JSON serializable") from exc
1254
+ if not isinstance(normalized, dict):
1255
+ raise ValidationError("selector artifact mapping must remain an object")
1256
+ return normalized
1257
+
1258
+
1259
+ def _family_sequence(value: Any) -> tuple[SemanticFamily, ...]:
1260
+ raw = _sequence(value, label="eligible families")
1261
+ try:
1262
+ families = tuple(SemanticFamily(item) for item in raw)
1263
+ except (TypeError, ValueError) as exc:
1264
+ raise ValidationError("eligible families contain an unknown semantic family") from exc
1265
+ if (
1266
+ len(families) != len(set(families))
1267
+ or any(family not in _CORE_FAMILIES for family in families)
1268
+ or families != tuple(family for family in _CORE_FAMILIES if family in families)
1269
+ ):
1270
+ raise ValidationError("eligible families must be canonical unique core families")
1271
+ return families
1272
+
1273
+
1274
+ def _family(value: Any) -> SemanticFamily:
1275
+ try:
1276
+ family = SemanticFamily(value)
1277
+ except (TypeError, ValueError) as exc:
1278
+ raise ValidationError("selector decision family is invalid") from exc
1279
+ if family not in {SemanticFamily.NO_OP, *_CORE_FAMILIES}:
1280
+ raise ValidationError("selector decision family is outside the campaign action set")
1281
+ return family
1282
+
1283
+
1284
+ def _strict_mapping(value: Any, keys: set[str], *, label: str) -> dict[str, Any]:
1285
+ if not isinstance(value, Mapping) or any(not isinstance(key, str) for key in value):
1286
+ raise ValidationError(f"{label} must be an object")
1287
+ data = dict(value)
1288
+ if set(data) != keys:
1289
+ raise ValidationError(f"{label} fields differ from the frozen schema")
1290
+ return data
1291
+
1292
+
1293
+ def _mapping(value: Any, *, label: str) -> dict[str, Any]:
1294
+ if not isinstance(value, Mapping) or any(not isinstance(key, str) for key in value):
1295
+ raise ValidationError(f"{label} must be an object")
1296
+ return dict(value)
1297
+
1298
+
1299
+ def _sequence(value: Any, *, label: str) -> tuple[Any, ...]:
1300
+ if isinstance(value, str | bytes) or not isinstance(value, Sequence):
1301
+ raise ValidationError(f"{label} must be an array")
1302
+ return tuple(value)
1303
+
1304
+
1305
+ def _text(value: Any, *, label: str) -> str:
1306
+ if not isinstance(value, str) or not value.strip() or value != value.strip():
1307
+ raise ValidationError(f"{label} must be a trimmed non-empty string")
1308
+ return value
1309
+
1310
+
1311
+ def _integer(value: Any, *, label: str) -> int:
1312
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
1313
+ raise ValidationError(f"{label} must be a non-negative integer")
1314
+ return value
1315
+
1316
+
1317
+ def _boolean(value: Any, *, label: str) -> bool:
1318
+ if not isinstance(value, bool):
1319
+ raise ValidationError(f"{label} must be boolean")
1320
+ return value
1321
+
1322
+
1323
+ def _require_sha256(value: Any, *, label: str) -> None:
1324
+ if not isinstance(value, str) or _SHA256.fullmatch(value) is None:
1325
+ raise ValidationError(f"{label} must be a lowercase SHA-256")
1326
+
1327
+
1328
+ def _sha256(value: Any, *, label: str) -> str:
1329
+ _require_sha256(value, label=label)
1330
+ return value
1331
+
1332
+
1333
+ def _group_id(value: Any, *, label: str) -> str:
1334
+ text = _text(value, label=label)
1335
+ if not text.startswith("group_") or _SHA256.fullmatch(text[6:]) is None:
1336
+ raise ValidationError(f"{label} must be a privacy-safe group hash")
1337
+ return text
1338
+
1339
+
1340
+ __all__ = [
1341
+ "assemble_selector_campaign_results",
1342
+ "build_selector_campaign_decisions",
1343
+ "build_selector_evaluation_input",
1344
+ "freeze_zero_shot_router_replay",
1345
+ "validate_selector_campaign_decisions",
1346
+ "validate_selector_evaluation_input",
1347
+ "validate_zero_shot_router_replay",
1348
+ ]