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,605 @@
1
+ """Side-effect-free manifest loading and explicit driver preflight checks.
2
+
3
+ Loading never imports operator code. Preflight is an explicit boundary that
4
+ may construct the selected driver and invoke its read-only ``preflight`` hook;
5
+ it never calls ``execute_cell``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import importlib
12
+ import json
13
+ import os
14
+ import re
15
+ import shutil
16
+ from collections.abc import Mapping, Sequence
17
+ from dataclasses import dataclass, field
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from agentinputlab.benchmarks import BenchmarkRegistry
22
+ from agentinputlab.contracts.errors import ArtifactError, ValidationError
23
+ from agentinputlab.contracts.serialization import content_hash, freeze_mapping
24
+ from agentinputlab.registry import AlgorithmRegistry
25
+
26
+ from .bindings import (
27
+ FrozenBindingReceipt,
28
+ build_guarded_driver_fingerprint,
29
+ resolve_frozen_binding_receipt,
30
+ )
31
+ from .parity_identity import (
32
+ ParityImplementationContract,
33
+ build_parity_implementation_contract,
34
+ )
35
+ from .schema import (
36
+ ExperimentArmKind,
37
+ ExperimentBenchmarkTarget,
38
+ ExperimentManifest,
39
+ ExperimentStage,
40
+ PrerequisiteKind,
41
+ require_sha256,
42
+ )
43
+ from .secrets import redact_secret_text, require_secret_free
44
+
45
+ _ENVIRONMENT_NAME = re.compile(r"^[A-Z_][A-Z0-9_]*$")
46
+
47
+
48
+ @dataclass(frozen=True, slots=True)
49
+ class PreflightReport:
50
+ study_id: str
51
+ manifest_hash: str
52
+ ready: bool
53
+ blockers: tuple[str, ...]
54
+ warnings: tuple[str, ...]
55
+ resolved_unit_counts: Mapping[str, int]
56
+ driver_entrypoint: str | None
57
+ binding_receipt: FrozenBindingReceipt | None = None
58
+ parity_contract: ParityImplementationContract | None = None
59
+ operator_driver_fingerprint: Mapping[str, Any] = field(
60
+ default_factory=dict,
61
+ repr=False,
62
+ )
63
+ guarded_driver_hash: str | None = None
64
+
65
+ def __post_init__(self) -> None:
66
+ object.__setattr__(self, "blockers", tuple(self.blockers))
67
+ object.__setattr__(self, "warnings", tuple(self.warnings))
68
+ object.__setattr__(
69
+ self,
70
+ "resolved_unit_counts",
71
+ freeze_mapping(self.resolved_unit_counts),
72
+ )
73
+ object.__setattr__(
74
+ self,
75
+ "operator_driver_fingerprint",
76
+ freeze_mapping(self.operator_driver_fingerprint),
77
+ )
78
+ if (
79
+ self.binding_receipt is not None
80
+ and self.binding_receipt.manifest_hash != self.manifest_hash
81
+ ):
82
+ raise ValidationError("preflight binding receipt belongs to another manifest")
83
+ if self.guarded_driver_hash is not None:
84
+ require_sha256(
85
+ self.guarded_driver_hash,
86
+ label="preflight guarded_driver_hash",
87
+ )
88
+ if self.ready and (
89
+ self.binding_receipt is None
90
+ or not self.operator_driver_fingerprint
91
+ or self.guarded_driver_hash is None
92
+ ):
93
+ raise ValidationError(
94
+ "ready preflight must bind its receipt and effective driver identity"
95
+ )
96
+
97
+ def to_dict(self) -> dict[str, Any]:
98
+ return {
99
+ "study_id": self.study_id,
100
+ "manifest_hash": self.manifest_hash,
101
+ "ready": self.ready,
102
+ "blockers": list(self.blockers),
103
+ "warnings": list(self.warnings),
104
+ "resolved_unit_counts": dict(self.resolved_unit_counts),
105
+ "driver_entrypoint": self.driver_entrypoint,
106
+ "binding_receipt": (
107
+ self.binding_receipt.to_dict() if self.binding_receipt is not None else None
108
+ ),
109
+ "parity_contract": (
110
+ self.parity_contract.to_dict() if self.parity_contract is not None else None
111
+ ),
112
+ "guarded_driver_hash": self.guarded_driver_hash,
113
+ }
114
+
115
+
116
+ def load_experiment_manifest(path: str | Path) -> ExperimentManifest:
117
+ """Load one strict JSON manifest without resolving operator resources."""
118
+
119
+ source = Path(path).expanduser().resolve()
120
+ try:
121
+ raw = json.loads(source.read_text(encoding="utf-8"))
122
+ except (OSError, UnicodeError, json.JSONDecodeError) as exc:
123
+ raise ArtifactError(f"experiment manifest is not valid UTF-8 JSON: {source}") from exc
124
+ try:
125
+ return ExperimentManifest.from_mapping(raw)
126
+ except ValidationError:
127
+ raise
128
+ except (TypeError, ValueError) as exc: # pragma: no cover - defensive boundary
129
+ raise ValidationError("invalid experiment manifest") from exc
130
+
131
+
132
+ def resolve_driver_entrypoint(entrypoint: str) -> Any:
133
+ """Import an operator-selected experiment driver only on explicit request."""
134
+
135
+ module_name, attribute_path = entrypoint.split(":", 1)
136
+ try:
137
+ value: Any = importlib.import_module(module_name)
138
+ for segment in attribute_path.split("."):
139
+ value = getattr(value, segment)
140
+ return value
141
+ except (ImportError, AttributeError) as exc:
142
+ raise ArtifactError(f"cannot resolve experiment driver {entrypoint!r}: {exc}") from exc
143
+
144
+
145
+ def preflight_experiment(
146
+ manifest: ExperimentManifest,
147
+ *,
148
+ driver_entrypoint: str | None = None,
149
+ environment: Mapping[str, str] | None = None,
150
+ resolve_units: bool = True,
151
+ ) -> tuple[PreflightReport, Mapping[str, tuple[str, ...]]]:
152
+ """Validate local prerequisites and invoke only the driver's preflight hook.
153
+
154
+ Environment values are deliberately never included in the report. File
155
+ paths are likewise represented only by the environment-variable name. A
156
+ user-selected zero-argument driver factory may be constructed, fingerprinted,
157
+ and asked for read-only blockers; ``execute_cell`` is never called.
158
+ """
159
+
160
+ env = dict(os.environ if environment is None else environment)
161
+ blockers: list[str] = []
162
+ warnings: list[str] = []
163
+ units: dict[str, tuple[str, ...]] = {}
164
+ benchmark_registry = BenchmarkRegistry.default()
165
+ algorithm_registry = AlgorithmRegistry.default()
166
+ effective_driver = driver_entrypoint or manifest.driver_entrypoint
167
+ binding_receipt: FrozenBindingReceipt | None = None
168
+ parity_contract: ParityImplementationContract | None = None
169
+ operator_fingerprint: Mapping[str, Any] = {}
170
+ guarded_driver_hash: str | None = None
171
+
172
+ revision_environment = manifest.metadata.get("source_revision_placeholder_must_match_env")
173
+ if revision_environment is not None:
174
+ if not isinstance(revision_environment, str) or not revision_environment:
175
+ blockers.append("source_revision_placeholder_must_match_env metadata is invalid")
176
+ else:
177
+ expected_revision = env.get(revision_environment)
178
+ if expected_revision:
179
+ for target in manifest.benchmarks:
180
+ if target.external and target.source_revision != expected_revision:
181
+ blockers.append(
182
+ f"benchmark {target.cell_id}: source revision is not bound "
183
+ f"to {revision_environment}; reload the blueprint after "
184
+ "setting the environment"
185
+ )
186
+
187
+ for target in manifest.benchmarks:
188
+ if not target.external:
189
+ try:
190
+ cell = benchmark_registry.get(target.cell_id)
191
+ except Exception as exc:
192
+ blockers.append(f"benchmark {target.cell_id}: {exc}")
193
+ else:
194
+ selected_arm_ids = set(target.arm_ids) or {arm.arm_id for arm in manifest.arms}
195
+ unsupported = sorted(
196
+ arm.arm_id
197
+ for arm in manifest.arms
198
+ if arm.arm_id in selected_arm_ids
199
+ and arm.arm_kind is ExperimentArmKind.RELEASE
200
+ and arm.family.value != "no_op"
201
+ and arm.family not in cell.families
202
+ )
203
+ if unsupported:
204
+ blockers.append(
205
+ f"benchmark {target.cell_id}: selected arms are outside the "
206
+ f"declared family support: {', '.join(unsupported)}"
207
+ )
208
+ declared_revision = manifest.metadata.get("benchmark_revisions", {})
209
+ if isinstance(declared_revision, Mapping):
210
+ pinned = declared_revision.get(target.cell_id)
211
+ if pinned is not None and pinned != cell.source_revision:
212
+ blockers.append(
213
+ f"benchmark {target.cell_id}: manifest revision differs from registry"
214
+ )
215
+ elif target.source_revision == "operator-pin-required-before-execution":
216
+ blockers.append(
217
+ f"benchmark {target.cell_id}: external source revision is still a placeholder"
218
+ )
219
+ if resolve_units:
220
+ try:
221
+ units[_target_key(target)] = resolve_target_units(target, environment=env)
222
+ except (ArtifactError, ValidationError) as exc:
223
+ blockers.append(f"benchmark {target.cell_id}: {exc}")
224
+
225
+ for arm in manifest.arms:
226
+ if arm.arm_kind is ExperimentArmKind.POLICY:
227
+ blockers.extend(_policy_arm_blockers(arm, manifest=manifest))
228
+ continue
229
+ if arm.release_id == "baseline.no_op":
230
+ continue
231
+ try:
232
+ method, release = algorithm_registry.get_release(arm.release_id)
233
+ except Exception as exc:
234
+ blockers.append(f"arm {arm.arm_id}: {exc}")
235
+ continue
236
+ if method.semantic_family is not arm.family:
237
+ blockers.append(
238
+ f"arm {arm.arm_id}: release belongs to {method.semantic_family.value}, "
239
+ f"not {arm.family.value}"
240
+ )
241
+ if arm.profile_kind not in release.profile_modes:
242
+ supported = ", ".join(mode.value for mode in release.profile_modes) or "none"
243
+ blockers.append(
244
+ f"arm {arm.arm_id}: release does not support "
245
+ f"{arm.profile_kind.value}; supported profile modes: {supported}"
246
+ )
247
+ if release.entrypoint is None:
248
+ reason = "; ".join(release.blocking_reasons) or "entrypoint is absent"
249
+ blockers.append(f"arm {arm.arm_id}: release is unavailable: {reason}")
250
+
251
+ prerequisite_blockers: list[str] = []
252
+ for prerequisite in manifest.prerequisites:
253
+ if prerequisite.kind is PrerequisiteKind.ENV:
254
+ if not env.get(prerequisite.name):
255
+ prerequisite_blockers.append(
256
+ f"missing environment prerequisite {prerequisite.name}: "
257
+ f"{prerequisite.description}"
258
+ )
259
+ elif prerequisite.kind is PrerequisiteKind.EXECUTABLE:
260
+ if shutil.which(prerequisite.name, path=env.get("PATH")) is None:
261
+ prerequisite_blockers.append(
262
+ f"missing executable prerequisite {prerequisite.name}: "
263
+ f"{prerequisite.description}"
264
+ )
265
+ elif prerequisite.kind in {
266
+ PrerequisiteKind.FILE_ENV,
267
+ PrerequisiteKind.DIRECTORY_ENV,
268
+ }:
269
+ raw = env.get(prerequisite.name)
270
+ if not raw:
271
+ prerequisite_blockers.append(
272
+ f"missing environment prerequisite {prerequisite.name}: "
273
+ f"{prerequisite.description}"
274
+ )
275
+ continue
276
+ candidate = Path(raw).expanduser()
277
+ expected_type = (
278
+ candidate.is_file()
279
+ if prerequisite.kind is PrerequisiteKind.FILE_ENV
280
+ else candidate.is_dir()
281
+ )
282
+ if not expected_type:
283
+ noun = "file" if prerequisite.kind is PrerequisiteKind.FILE_ENV else "directory"
284
+ prerequisite_blockers.append(
285
+ f"{prerequisite.name} does not identify an existing {noun}"
286
+ )
287
+ continue
288
+ if prerequisite.sha256_env is not None:
289
+ expected = env.get(prerequisite.sha256_env)
290
+ if not expected:
291
+ prerequisite_blockers.append(
292
+ f"missing checksum prerequisite {prerequisite.sha256_env}"
293
+ )
294
+ else:
295
+ try:
296
+ require_sha256(
297
+ expected,
298
+ label=f"environment {prerequisite.sha256_env}",
299
+ )
300
+ except ValidationError as exc:
301
+ prerequisite_blockers.append(str(exc))
302
+ else:
303
+ actual = _sha256_file(candidate)
304
+ if actual != expected:
305
+ prerequisite_blockers.append(
306
+ f"{prerequisite.name} checksum differs from "
307
+ f"{prerequisite.sha256_env}"
308
+ )
309
+ blockers.extend(prerequisite_blockers)
310
+ if not prerequisite_blockers:
311
+ try:
312
+ binding_receipt = resolve_frozen_binding_receipt(
313
+ manifest,
314
+ environment=env,
315
+ )
316
+ except (ArtifactError, ValidationError) as exc:
317
+ blockers.append(f"frozen binding receipt: {exc}")
318
+
319
+ if effective_driver is None:
320
+ blockers.append("experiment driver is not configured; pass --driver module:attribute")
321
+ else:
322
+ try:
323
+ driver_factory = resolve_driver_entrypoint(effective_driver)
324
+ except ArtifactError as exc:
325
+ blockers.append(str(exc))
326
+ else:
327
+ driver_blockers, operator_fingerprint = _preflight_driver(
328
+ driver_factory,
329
+ manifest=manifest,
330
+ resolved_units=(units if len(units) == len(manifest.benchmarks) else None),
331
+ )
332
+ blockers.extend(driver_blockers)
333
+ if operator_fingerprint and binding_receipt is not None:
334
+ if manifest.stage is ExperimentStage.PARITY:
335
+ try:
336
+ parity_contract = build_parity_implementation_contract(
337
+ manifest,
338
+ binding_receipt,
339
+ operator_fingerprint,
340
+ )
341
+ except (ArtifactError, ValidationError) as exc:
342
+ blockers.append(f"parity implementation identity: {exc}")
343
+ if manifest.stage is not ExperimentStage.PARITY or parity_contract is not None:
344
+ guarded_fingerprint = build_guarded_driver_fingerprint(
345
+ operator_fingerprint,
346
+ binding_receipt,
347
+ parity_contract=(
348
+ parity_contract.to_dict() if parity_contract is not None else None
349
+ ),
350
+ )
351
+ guarded_driver_hash = content_hash(guarded_fingerprint)
352
+
353
+ if not resolve_units:
354
+ warnings.append("unit inventories were not materialized")
355
+ blockers = sorted(set(blockers))
356
+ warnings = sorted(set(warnings))
357
+ report = PreflightReport(
358
+ study_id=manifest.study_id,
359
+ manifest_hash=manifest.manifest_hash,
360
+ ready=not blockers,
361
+ blockers=tuple(blockers),
362
+ warnings=tuple(warnings),
363
+ resolved_unit_counts={key: len(value) for key, value in sorted(units.items())},
364
+ driver_entrypoint=effective_driver,
365
+ binding_receipt=binding_receipt,
366
+ parity_contract=parity_contract,
367
+ operator_driver_fingerprint=operator_fingerprint,
368
+ guarded_driver_hash=guarded_driver_hash,
369
+ )
370
+ return report, freeze_mapping(units)
371
+
372
+
373
+ def resolve_target_units(
374
+ target: ExperimentBenchmarkTarget,
375
+ *,
376
+ environment: Mapping[str, str] | None = None,
377
+ ) -> tuple[str, ...]:
378
+ if target.unit_ids:
379
+ return target.unit_ids
380
+ env = os.environ if environment is None else environment
381
+ variable = target.unit_inventory_env
382
+ if variable is None: # pragma: no cover - schema invariant
383
+ raise ValidationError("benchmark target has no unit source")
384
+ raw_path = env.get(variable)
385
+ if not raw_path:
386
+ raise ArtifactError(f"unit inventory environment {variable} is not set")
387
+ path = Path(raw_path).expanduser()
388
+ if not path.is_file():
389
+ raise ArtifactError(f"unit inventory from {variable} is not an existing file")
390
+ if target.unit_inventory_sha256_env is not None:
391
+ checksum_name = target.unit_inventory_sha256_env
392
+ expected = env.get(checksum_name)
393
+ if not expected:
394
+ raise ArtifactError(f"unit inventory checksum environment {checksum_name} is not set")
395
+ require_sha256(expected, label=f"environment {checksum_name}")
396
+ if _sha256_file(path) != expected:
397
+ raise ArtifactError(f"unit inventory from {variable} does not match {checksum_name}")
398
+ try:
399
+ value = json.loads(path.read_text(encoding="utf-8"))
400
+ except (OSError, UnicodeError, json.JSONDecodeError) as exc:
401
+ raise ArtifactError(f"unit inventory from {variable} is not valid UTF-8 JSON") from exc
402
+ if (
403
+ not isinstance(value, list)
404
+ or not value
405
+ or any(not isinstance(item, str) or not item.strip() for item in value)
406
+ ):
407
+ raise ArtifactError(f"unit inventory from {variable} must be a non-empty string array")
408
+ if len(value) != len(set(value)):
409
+ raise ArtifactError(f"unit inventory from {variable} contains duplicate IDs")
410
+ return tuple(value)
411
+
412
+
413
+ def _target_key(target: ExperimentBenchmarkTarget) -> str:
414
+ return f"{target.cell_id}:{target.split}"
415
+
416
+
417
+ def _sha256_file(path: Path) -> str:
418
+ digest = hashlib.sha256()
419
+ with path.open("rb") as handle:
420
+ for block in iter(lambda: handle.read(1024 * 1024), b""):
421
+ digest.update(block)
422
+ return digest.hexdigest()
423
+
424
+
425
+ def _preflight_driver(
426
+ factory: Any,
427
+ *,
428
+ manifest: ExperimentManifest,
429
+ resolved_units: Mapping[str, tuple[str, ...]] | None,
430
+ ) -> tuple[tuple[str, ...], Mapping[str, Any]]:
431
+ """Construct a selected driver and run its read-only preflight contract."""
432
+
433
+ if not callable(factory):
434
+ return (
435
+ ("experiment driver entrypoint must be a zero-argument factory",),
436
+ {},
437
+ )
438
+ try:
439
+ driver = factory()
440
+ except Exception as exc:
441
+ return (
442
+ ("experiment driver factory failed during preflight: " + _redacted_message(exc),),
443
+ {},
444
+ )
445
+ fingerprint = getattr(driver, "fingerprint", None)
446
+ driver_preflight = getattr(driver, "preflight", None)
447
+ execute_cell = getattr(driver, "execute_cell", None)
448
+ if not callable(fingerprint) or not callable(driver_preflight) or not callable(execute_cell):
449
+ return (
450
+ ("experiment driver does not implement the required driver protocol",),
451
+ {},
452
+ )
453
+ try:
454
+ frozen_fingerprint = freeze_mapping(fingerprint())
455
+ except Exception as exc:
456
+ return (
457
+ ("experiment driver fingerprint is invalid: " + _redacted_message(exc),),
458
+ {},
459
+ )
460
+ if not frozen_fingerprint:
461
+ return (("experiment driver fingerprint must not be empty",), {})
462
+ try:
463
+ require_secret_free(
464
+ frozen_fingerprint,
465
+ label="experiment driver fingerprint",
466
+ )
467
+ except ArtifactError as exc:
468
+ return ((_redacted_message(exc),), {})
469
+ try:
470
+ raw_blockers = driver_preflight(manifest)
471
+ except Exception as exc:
472
+ return (
473
+ ("experiment driver preflight failed: " + _redacted_message(exc),),
474
+ frozen_fingerprint,
475
+ )
476
+ if (
477
+ isinstance(raw_blockers, (str, bytes))
478
+ or not isinstance(raw_blockers, Sequence)
479
+ or any(not isinstance(item, str) or not item.strip() for item in raw_blockers)
480
+ ):
481
+ return (
482
+ ("experiment driver preflight returned invalid blockers",),
483
+ frozen_fingerprint,
484
+ )
485
+ blockers = ["driver preflight: " + _redacted_text(item[:2048]) for item in raw_blockers]
486
+ unit_hook = getattr(driver, "preflight_units", None)
487
+ require_unit_hook = manifest.metadata.get("require_driver_unit_preflight") is True
488
+ if resolved_units is not None:
489
+ if unit_hook is None and require_unit_hook:
490
+ blockers.append(
491
+ "driver preflight: manifest requires preflight_units(), but the "
492
+ "selected driver does not implement it"
493
+ )
494
+ elif unit_hook is not None:
495
+ if not callable(unit_hook):
496
+ blockers.append("driver preflight: preflight_units must be callable")
497
+ else:
498
+ try:
499
+ raw_unit_blockers = unit_hook(manifest, resolved_units)
500
+ except Exception as exc:
501
+ blockers.append("driver unit preflight failed: " + _redacted_message(exc))
502
+ else:
503
+ if (
504
+ isinstance(raw_unit_blockers, (str, bytes))
505
+ or not isinstance(raw_unit_blockers, Sequence)
506
+ or any(
507
+ not isinstance(item, str) or not item.strip()
508
+ for item in raw_unit_blockers
509
+ )
510
+ ):
511
+ blockers.append(
512
+ "driver preflight: preflight_units returned invalid blockers"
513
+ )
514
+ else:
515
+ blockers.extend(
516
+ "driver unit preflight: " + _redacted_text(item[:2048])
517
+ for item in raw_unit_blockers
518
+ )
519
+ return tuple(blockers), frozen_fingerprint
520
+
521
+
522
+ def _policy_arm_blockers(
523
+ arm: Any,
524
+ *,
525
+ manifest: ExperimentManifest,
526
+ ) -> tuple[str, ...]:
527
+ """Validate frozen policy bindings without opening evaluator outcomes."""
528
+
529
+ specifications = {
530
+ "policy.locked_best_eligible_global": {
531
+ "policy_kind": "locked_best_eligible_global",
532
+ "required_parameters": {
533
+ "policy_kind",
534
+ "policy_manifest_env",
535
+ "policy_manifest_sha256_env",
536
+ },
537
+ "required_prerequisites": {
538
+ ("file_env", "policy_manifest_env", "policy_manifest_sha256_env"),
539
+ },
540
+ },
541
+ "policy.eligibility_aware_selector": {
542
+ "policy_kind": "frozen_selector",
543
+ "required_parameters": {
544
+ "policy_kind",
545
+ "policy_manifest_env",
546
+ "policy_manifest_sha256_env",
547
+ "selector_identity_env",
548
+ "selector_bundle_env",
549
+ "selector_bundle_sha256_env",
550
+ },
551
+ "required_prerequisites": {
552
+ ("file_env", "policy_manifest_env", "policy_manifest_sha256_env"),
553
+ ("file_env", "selector_bundle_env", "selector_bundle_sha256_env"),
554
+ ("env", "selector_identity_env", None),
555
+ },
556
+ },
557
+ }
558
+ try:
559
+ specification = specifications[arm.release_id]
560
+ except KeyError:
561
+ return (f"arm {arm.arm_id}: unsupported frozen policy identity",)
562
+ parameters = dict(arm.parameters)
563
+ if set(parameters) != specification["required_parameters"]:
564
+ return (f"arm {arm.arm_id}: frozen policy binding fields are invalid",)
565
+ if parameters.get("policy_kind") != specification["policy_kind"]:
566
+ return (f"arm {arm.arm_id}: frozen policy kind differs from its identity",)
567
+ prerequisites = {
568
+ (item.kind.value, item.name, item.sha256_env) for item in manifest.prerequisites
569
+ }
570
+ blockers: list[str] = []
571
+ for kind, name_key, checksum_key in specification["required_prerequisites"]:
572
+ name = parameters.get(name_key)
573
+ checksum = parameters.get(checksum_key) if checksum_key is not None else None
574
+ if (
575
+ not isinstance(name, str)
576
+ or not _ENVIRONMENT_NAME.fullmatch(name)
577
+ or (
578
+ checksum_key is not None
579
+ and (not isinstance(checksum, str) or not _ENVIRONMENT_NAME.fullmatch(checksum))
580
+ )
581
+ ):
582
+ blockers.append(f"arm {arm.arm_id}: frozen policy environment binding is invalid")
583
+ continue
584
+ if (kind, name, checksum) not in prerequisites:
585
+ blockers.append(
586
+ f"arm {arm.arm_id}: frozen policy binding {name} is not a matching prerequisite"
587
+ )
588
+ return tuple(blockers)
589
+
590
+
591
+ def _redacted_message(error: BaseException) -> str:
592
+ return _redacted_text(str(error)[:2048])
593
+
594
+
595
+ def _redacted_text(value: str) -> str:
596
+ return redact_secret_text(value)
597
+
598
+
599
+ __all__ = [
600
+ "PreflightReport",
601
+ "load_experiment_manifest",
602
+ "preflight_experiment",
603
+ "resolve_driver_entrypoint",
604
+ "resolve_target_units",
605
+ ]