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,203 @@
1
+ """Capability-locked 27-method by seven-static-dataset execution matrix.
2
+
3
+ The matrix distinguishes structural eligibility from execution evidence. A
4
+ static benchmark is eligible only when it declares the method's semantic
5
+ family. Ineligible pairs remain explicit rows; they are never converted into
6
+ zero scores or silently omitted from denominators.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from collections.abc import Mapping
13
+ from dataclasses import dataclass
14
+ from importlib import resources
15
+ from types import MappingProxyType
16
+ from typing import Any
17
+
18
+ from agentinputlab.benchmarks import BenchmarkRegistry, production_benchmark_specs
19
+ from agentinputlab.contracts.enums import ProfileKind
20
+ from agentinputlab.contracts.errors import ArtifactError, ValidationError
21
+ from agentinputlab.contracts.serialization import content_hash, freeze_mapping
22
+ from agentinputlab.registry import AlgorithmRegistry
23
+
24
+ AUXILIARY_MATRIX_PROTOCOL = "agentinputlab.auxiliary-method-cell-matrix.v1"
25
+ AUXILIARY_PROFILE_PROTOCOL = "agentinputlab.auxiliary-method-profiles.v1"
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class AuxiliaryMethodProfile:
30
+ method_id: str
31
+ release_id: str
32
+ profile_kind: ProfileKind
33
+
34
+ def __post_init__(self) -> None:
35
+ for label, value in (
36
+ ("method_id", self.method_id),
37
+ ("release_id", self.release_id),
38
+ ):
39
+ if not isinstance(value, str) or not value.strip():
40
+ raise ValidationError(f"auxiliary profile {label} must be non-empty")
41
+ if not isinstance(self.profile_kind, ProfileKind):
42
+ raise TypeError("auxiliary profile_kind must be ProfileKind")
43
+
44
+
45
+ def auxiliary_static_cells() -> Mapping[str, str]:
46
+ """Return the exact seven production static cells and frozen splits."""
47
+
48
+ cells = {
49
+ spec.cell_id: spec.split
50
+ for spec in production_benchmark_specs()
51
+ if spec.adapter_kind in {"retrieval", "qa"}
52
+ }
53
+ if len(cells) != 7:
54
+ raise ValidationError(f"auxiliary matrix requires seven static cells, got {len(cells)}")
55
+ return MappingProxyType(dict(sorted(cells.items())))
56
+
57
+
58
+ def load_auxiliary_method_profiles() -> Mapping[str, AuxiliaryMethodProfile]:
59
+ """Load and validate the packaged method-to-executable-profile selection."""
60
+
61
+ try:
62
+ source = resources.files("agentinputlab.registry").joinpath(
63
+ "data/auxiliary_method_profiles.json"
64
+ )
65
+ raw_bytes = source.read_bytes()
66
+ raw = json.loads(raw_bytes.decode("utf-8"))
67
+ except (OSError, UnicodeError, json.JSONDecodeError) as exc:
68
+ raise ArtifactError("auxiliary method profile catalog is unavailable") from exc
69
+ if (
70
+ not isinstance(raw, dict)
71
+ or set(raw) != {"schema_version", "protocol", "method_count", "methods"}
72
+ or raw.get("schema_version") != 1
73
+ or raw.get("protocol") != AUXILIARY_PROFILE_PROTOCOL
74
+ or not isinstance(raw.get("methods"), list)
75
+ or raw.get("method_count") != len(raw["methods"])
76
+ ):
77
+ raise ValidationError("auxiliary method profile catalog schema is invalid")
78
+
79
+ registry = AlgorithmRegistry.default()
80
+ profiles: dict[str, AuxiliaryMethodProfile] = {}
81
+ for index, item in enumerate(raw["methods"]):
82
+ if not isinstance(item, dict) or set(item) != {
83
+ "method_id",
84
+ "release_id",
85
+ "profile_kind",
86
+ }:
87
+ raise ValidationError(f"auxiliary method profile row {index} is invalid")
88
+ try:
89
+ profile = AuxiliaryMethodProfile(
90
+ method_id=item["method_id"],
91
+ release_id=item["release_id"],
92
+ profile_kind=ProfileKind(item["profile_kind"]),
93
+ )
94
+ method, release = registry.get_release(profile.release_id)
95
+ except (KeyError, TypeError, ValueError) as exc:
96
+ raise ValidationError(
97
+ f"auxiliary method profile row {index} cannot be resolved"
98
+ ) from exc
99
+ if profile.method_id in profiles:
100
+ raise ValidationError("auxiliary method profile catalog contains duplicates")
101
+ if method.method_id != profile.method_id:
102
+ raise ValidationError("auxiliary profile release belongs to another method")
103
+ if profile.profile_kind not in release.profile_modes:
104
+ raise ValidationError("auxiliary profile kind is unsupported by its release")
105
+ if release.entrypoint is None:
106
+ raise ValidationError("auxiliary profile release has no executable entrypoint")
107
+ profiles[profile.method_id] = profile
108
+
109
+ registered = {method.method_id for method in registry.list_methods()}
110
+ if set(profiles) != registered or len(profiles) != 27:
111
+ raise ValidationError("auxiliary profiles must cover the exact 27-method registry")
112
+ return MappingProxyType(dict(sorted(profiles.items())))
113
+
114
+
115
+ def build_auxiliary_method_cell_matrix() -> Mapping[str, Any]:
116
+ """Build the deterministic 27x7 eligibility and launch matrix."""
117
+
118
+ registry = AlgorithmRegistry.default()
119
+ benchmark_registry = BenchmarkRegistry.default()
120
+ profiles = load_auxiliary_method_profiles()
121
+ cells = auxiliary_static_cells()
122
+ rows: list[dict[str, Any]] = []
123
+ family_method_counts: dict[str, int] = {}
124
+ for method in sorted(registry.list_methods(), key=lambda value: value.method_id):
125
+ family = method.semantic_family.value
126
+ family_method_counts[family] = family_method_counts.get(family, 0) + 1
127
+ profile = profiles[method.method_id]
128
+ for cell_id, split in cells.items():
129
+ cell = benchmark_registry.get(cell_id)
130
+ eligible = method.semantic_family in cell.families
131
+ reason = (
132
+ None
133
+ if eligible
134
+ else (
135
+ f"{cell_id} does not declare {method.semantic_family.value} support; "
136
+ "the pair is structurally ineligible"
137
+ )
138
+ )
139
+ rows.append(
140
+ {
141
+ "method_id": method.method_id,
142
+ "display_name": method.display_name,
143
+ "release_id": profile.release_id,
144
+ "profile_kind": profile.profile_kind.value,
145
+ "family": family,
146
+ "benchmark_cell_id": cell_id,
147
+ "split": split,
148
+ "benchmark_family_support": tuple(
149
+ item.value for item in sorted(cell.families, key=lambda value: value.value)
150
+ ),
151
+ "eligible": eligible,
152
+ "state": "launchable" if eligible else "structurally_ineligible",
153
+ "reason": reason,
154
+ "one_click_command": (
155
+ "python scripts/experiments/run_auxiliary_method_cell.py "
156
+ "<frozen-campaign.json> "
157
+ f"--method {method.method_id} --cell {cell_id}"
158
+ if eligible
159
+ else None
160
+ ),
161
+ }
162
+ )
163
+ eligible_count = sum(bool(row["eligible"]) for row in rows)
164
+ if len(rows) != 189 or eligible_count != 117:
165
+ raise ValidationError(
166
+ f"auxiliary matrix invariant failed: rows={len(rows)}, eligible={eligible_count}"
167
+ )
168
+ payload: dict[str, Any] = {
169
+ "schema_version": 1,
170
+ "protocol": AUXILIARY_MATRIX_PROTOCOL,
171
+ "record_type": "EXECUTION_DESIGN",
172
+ "observed_results": False,
173
+ "method_count": 27,
174
+ "dataset_count": 7,
175
+ "row_count": len(rows),
176
+ "eligible_pair_count": eligible_count,
177
+ "structurally_ineligible_pair_count": len(rows) - eligible_count,
178
+ "family_method_counts": dict(sorted(family_method_counts.items())),
179
+ "cells": dict(cells),
180
+ "rows": rows,
181
+ }
182
+ payload["matrix_hash"] = content_hash(payload)
183
+ return freeze_mapping(payload)
184
+
185
+
186
+ def auxiliary_pair(method_id: str, cell_id: str) -> Mapping[str, Any]:
187
+ """Resolve one row and reject unknown identities without guessing."""
188
+
189
+ for row in build_auxiliary_method_cell_matrix()["rows"]:
190
+ if row["method_id"] == method_id and row["benchmark_cell_id"] == cell_id:
191
+ return row
192
+ raise KeyError(f"unknown auxiliary method-cell pair: {method_id} x {cell_id}")
193
+
194
+
195
+ __all__ = [
196
+ "AUXILIARY_MATRIX_PROTOCOL",
197
+ "AUXILIARY_PROFILE_PROTOCOL",
198
+ "AuxiliaryMethodProfile",
199
+ "auxiliary_pair",
200
+ "auxiliary_static_cells",
201
+ "build_auxiliary_method_cell_matrix",
202
+ "load_auxiliary_method_profiles",
203
+ ]
@@ -0,0 +1,430 @@
1
+ """Content-bound receipts for assets admitted by experiment preflight.
2
+
3
+ The manifest deliberately stores environment-variable *names*, not machine-local
4
+ paths or secret values. This module resolves those names at preflight time and
5
+ turns every admitted prerequisite into a canonical, secret-safe identity. The
6
+ resulting receipt is embedded in the effective experiment-driver fingerprint.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hashlib
12
+ import os
13
+ import shutil
14
+ import stat
15
+ import subprocess
16
+ from collections.abc import Mapping
17
+ from dataclasses import dataclass
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from agentinputlab.contracts.errors import ArtifactError, ValidationError
22
+ from agentinputlab.contracts.serialization import (
23
+ content_hash,
24
+ freeze_mapping,
25
+ to_jsonable,
26
+ )
27
+
28
+ from .schema import ExperimentManifest, PrerequisiteKind, require_sha256
29
+
30
+ _GIT_OBJECT_ID_LENGTHS = frozenset({40, 64})
31
+ _CHECKOUT_SUFFIX = "_CHECKOUT"
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class FrozenBindingEntry:
36
+ """One prerequisite identity without its raw environment value."""
37
+
38
+ name: str
39
+ kind: str
40
+ identity: Mapping[str, Any]
41
+ schema_version: int = 1
42
+
43
+ def __post_init__(self) -> None:
44
+ if self.schema_version != 1:
45
+ raise ValidationError("unsupported frozen binding entry schema")
46
+ if not isinstance(self.name, str) or not self.name.strip():
47
+ raise ValidationError("frozen binding entry requires a name")
48
+ if not isinstance(self.kind, str) or not self.kind.strip():
49
+ raise ValidationError("frozen binding entry requires a kind")
50
+ try:
51
+ identity = freeze_mapping(self.identity)
52
+ except (TypeError, ValueError) as exc:
53
+ raise ValidationError("frozen binding identity must be canonical JSON") from exc
54
+ if not identity:
55
+ raise ValidationError("frozen binding identity must not be empty")
56
+ object.__setattr__(self, "identity", identity)
57
+
58
+ @property
59
+ def binding_hash(self) -> str:
60
+ return content_hash(
61
+ {
62
+ "schema_version": self.schema_version,
63
+ "name": self.name,
64
+ "kind": self.kind,
65
+ "identity": self.identity,
66
+ }
67
+ )
68
+
69
+ def to_dict(self) -> dict[str, Any]:
70
+ return {
71
+ "schema_version": self.schema_version,
72
+ "name": self.name,
73
+ "kind": self.kind,
74
+ "identity": to_jsonable(self.identity),
75
+ "binding_hash": self.binding_hash,
76
+ }
77
+
78
+
79
+ @dataclass(frozen=True, slots=True)
80
+ class FrozenBindingReceipt:
81
+ """Canonical receipt for all manifest prerequisites accepted by preflight."""
82
+
83
+ manifest_hash: str
84
+ bindings: tuple[FrozenBindingEntry, ...]
85
+ schema_version: int = 1
86
+
87
+ def __post_init__(self) -> None:
88
+ if self.schema_version != 1:
89
+ raise ValidationError("unsupported frozen binding receipt schema")
90
+ require_sha256(self.manifest_hash, label="binding receipt manifest_hash")
91
+ bindings = tuple(self.bindings)
92
+ if any(not isinstance(item, FrozenBindingEntry) for item in bindings):
93
+ raise ValidationError("frozen binding receipt entries must be typed")
94
+ identities = [(item.kind, item.name) for item in bindings]
95
+ if len(identities) != len(set(identities)):
96
+ raise ValidationError("frozen binding receipt entries must be unique")
97
+ canonical = tuple(sorted(bindings, key=lambda item: (item.kind, item.name)))
98
+ object.__setattr__(self, "bindings", canonical)
99
+
100
+ @property
101
+ def receipt_hash(self) -> str:
102
+ return content_hash(
103
+ {
104
+ "schema_version": self.schema_version,
105
+ "manifest_hash": self.manifest_hash,
106
+ "bindings": [
107
+ {
108
+ "schema_version": item.schema_version,
109
+ "name": item.name,
110
+ "kind": item.kind,
111
+ "identity": item.identity,
112
+ }
113
+ for item in self.bindings
114
+ ],
115
+ }
116
+ )
117
+
118
+ def binding(self, name: str, *, kind: str | None = None) -> FrozenBindingEntry:
119
+ matches = tuple(
120
+ item
121
+ for item in self.bindings
122
+ if item.name == name and (kind is None or item.kind == kind)
123
+ )
124
+ if len(matches) != 1:
125
+ raise ArtifactError(f"frozen binding receipt has no unique entry for {name!r}")
126
+ return matches[0]
127
+
128
+ def to_dict(self) -> dict[str, Any]:
129
+ return {
130
+ "schema_version": self.schema_version,
131
+ "manifest_hash": self.manifest_hash,
132
+ "bindings": [item.to_dict() for item in self.bindings],
133
+ "receipt_hash": self.receipt_hash,
134
+ }
135
+
136
+
137
+ def resolve_frozen_binding_receipt(
138
+ manifest: ExperimentManifest,
139
+ *,
140
+ environment: Mapping[str, str] | None = None,
141
+ ) -> FrozenBindingReceipt:
142
+ """Resolve and independently attest every declared prerequisite.
143
+
144
+ Checkout directories are required to be clean Git roots. Other directories
145
+ receive a deterministic content-tree hash. Raw paths and environment values
146
+ never enter the receipt; only their SHA-256 identities do.
147
+ """
148
+
149
+ if not isinstance(manifest, ExperimentManifest):
150
+ raise TypeError("binding resolution requires ExperimentManifest")
151
+ env = dict(os.environ if environment is None else environment)
152
+ expectations = _checkout_expectations(manifest, environment=env)
153
+ declared_directories = {
154
+ item.name for item in manifest.prerequisites if item.kind is PrerequisiteKind.DIRECTORY_ENV
155
+ }
156
+ unknown_expectations = sorted(set(expectations) - declared_directories)
157
+ if unknown_expectations:
158
+ raise ValidationError(
159
+ "checkout_expected_revisions references undeclared directories: "
160
+ + ", ".join(unknown_expectations)
161
+ )
162
+
163
+ bindings: list[FrozenBindingEntry] = []
164
+ for prerequisite in manifest.prerequisites:
165
+ if prerequisite.kind is PrerequisiteKind.ENV:
166
+ raw = env.get(prerequisite.name)
167
+ if not raw:
168
+ raise ArtifactError(f"missing environment prerequisite {prerequisite.name}")
169
+ encoded = raw.encode("utf-8")
170
+ identity = {
171
+ "identity_mode": "value_sha256",
172
+ "value_sha256": hashlib.sha256(encoded).hexdigest(),
173
+ "value_bytes": len(encoded),
174
+ }
175
+ elif prerequisite.kind is PrerequisiteKind.EXECUTABLE:
176
+ executable = shutil.which(prerequisite.name, path=env.get("PATH"))
177
+ if executable is None:
178
+ raise ArtifactError(f"missing executable prerequisite {prerequisite.name}")
179
+ candidate = Path(executable).expanduser().resolve()
180
+ if not candidate.is_file():
181
+ raise ArtifactError(f"executable prerequisite {prerequisite.name} is not a file")
182
+ identity = {
183
+ "identity_mode": "executable_sha256",
184
+ "resolved_path_sha256": _sha256_text(str(candidate)),
185
+ "content_sha256": _sha256_file(candidate),
186
+ "size_bytes": candidate.stat().st_size,
187
+ }
188
+ else:
189
+ raw = env.get(prerequisite.name)
190
+ if not raw:
191
+ raise ArtifactError(f"missing environment prerequisite {prerequisite.name}")
192
+ candidate = Path(raw).expanduser().resolve()
193
+ if prerequisite.kind is PrerequisiteKind.FILE_ENV:
194
+ if not candidate.is_file():
195
+ raise ArtifactError(f"{prerequisite.name} does not identify an existing file")
196
+ actual = _sha256_file(candidate)
197
+ if prerequisite.sha256_env is not None:
198
+ expected = env.get(prerequisite.sha256_env)
199
+ if not expected:
200
+ raise ArtifactError(
201
+ f"missing checksum prerequisite {prerequisite.sha256_env}"
202
+ )
203
+ require_sha256(
204
+ expected,
205
+ label=f"environment {prerequisite.sha256_env}",
206
+ )
207
+ if actual != expected:
208
+ raise ArtifactError(
209
+ f"{prerequisite.name} checksum differs from {prerequisite.sha256_env}"
210
+ )
211
+ identity = {
212
+ "identity_mode": "file_sha256",
213
+ "resolved_path_sha256": _sha256_text(str(candidate)),
214
+ "content_sha256": actual,
215
+ "size_bytes": candidate.stat().st_size,
216
+ "checksum_environment": prerequisite.sha256_env,
217
+ }
218
+ else:
219
+ if not candidate.is_dir():
220
+ raise ArtifactError(
221
+ f"{prerequisite.name} does not identify an existing directory"
222
+ )
223
+ expected_revision = expectations.get(prerequisite.name)
224
+ if expected_revision is not None or prerequisite.name.endswith(_CHECKOUT_SUFFIX):
225
+ identity = _git_checkout_identity(
226
+ candidate,
227
+ expected_revision=expected_revision,
228
+ )
229
+ else:
230
+ identity = {
231
+ "identity_mode": "directory_tree_sha256",
232
+ "resolved_path_sha256": _sha256_text(str(candidate)),
233
+ "tree_sha256": _sha256_directory_tree(candidate),
234
+ }
235
+ bindings.append(
236
+ FrozenBindingEntry(
237
+ name=prerequisite.name,
238
+ kind=prerequisite.kind.value,
239
+ identity=identity,
240
+ )
241
+ )
242
+ return FrozenBindingReceipt(
243
+ manifest_hash=manifest.manifest_hash,
244
+ bindings=tuple(bindings),
245
+ )
246
+
247
+
248
+ def build_guarded_driver_fingerprint(
249
+ operator_fingerprint: Mapping[str, Any],
250
+ binding_receipt: FrozenBindingReceipt,
251
+ *,
252
+ parity_contract: Mapping[str, Any] | None = None,
253
+ ) -> Mapping[str, Any]:
254
+ """Build the exact fingerprint persisted by the guarded runtime driver."""
255
+
256
+ if not isinstance(binding_receipt, FrozenBindingReceipt):
257
+ raise TypeError("guarded fingerprint requires FrozenBindingReceipt")
258
+ try:
259
+ operator = freeze_mapping(operator_fingerprint)
260
+ contract = freeze_mapping(parity_contract or {})
261
+ except (TypeError, ValueError) as exc:
262
+ raise ValidationError("guarded driver identity must be canonical JSON") from exc
263
+ if not operator:
264
+ raise ValidationError("operator driver fingerprint must not be empty")
265
+ return freeze_mapping(
266
+ {
267
+ "schema_version": 1,
268
+ "guard": "agentinputlab.frozen_binding_driver_guard",
269
+ "operator_driver_fingerprint": operator,
270
+ "frozen_binding_receipt": binding_receipt.to_dict(),
271
+ "frozen_binding_receipt_hash": binding_receipt.receipt_hash,
272
+ "parity_contract": contract,
273
+ }
274
+ )
275
+
276
+
277
+ def _checkout_expectations(
278
+ manifest: ExperimentManifest,
279
+ *,
280
+ environment: Mapping[str, str],
281
+ ) -> dict[str, str]:
282
+ raw = manifest.metadata.get("checkout_expected_revisions", {})
283
+ if not isinstance(raw, Mapping):
284
+ raise ValidationError("checkout_expected_revisions metadata must be an object")
285
+ result: dict[str, str] = {}
286
+ for name, revision in raw.items():
287
+ if not isinstance(name, str) or not name:
288
+ raise ValidationError("checkout expectation names must be non-empty strings")
289
+ if (
290
+ not isinstance(revision, str)
291
+ or len(revision) not in _GIT_OBJECT_ID_LENGTHS
292
+ or any(character not in "0123456789abcdef" for character in revision)
293
+ ):
294
+ raise ValidationError(
295
+ f"checkout expectation {name} must be a full lowercase Git object ID"
296
+ )
297
+ result[name] = revision
298
+
299
+ dynamic = manifest.metadata.get("checkout_expected_revision_from_env", {})
300
+ if not isinstance(dynamic, Mapping):
301
+ raise ValidationError("checkout_expected_revision_from_env metadata must be an object")
302
+ declared_value_environments = {
303
+ item.name for item in manifest.prerequisites if item.kind is PrerequisiteKind.ENV
304
+ }
305
+ for checkout_environment, revision_environment in dynamic.items():
306
+ if not isinstance(checkout_environment, str) or not checkout_environment:
307
+ raise ValidationError("dynamic checkout expectation names must be non-empty strings")
308
+ if not isinstance(revision_environment, str) or not revision_environment:
309
+ raise ValidationError(
310
+ "dynamic checkout revision environments must be non-empty strings"
311
+ )
312
+ if revision_environment not in declared_value_environments:
313
+ raise ValidationError(
314
+ "checkout_expected_revision_from_env references an undeclared "
315
+ f"value prerequisite: {revision_environment}"
316
+ )
317
+ revision = environment.get(revision_environment)
318
+ if not revision:
319
+ raise ArtifactError(f"missing checkout revision prerequisite {revision_environment}")
320
+ if (
321
+ not isinstance(revision, str)
322
+ or len(revision) not in _GIT_OBJECT_ID_LENGTHS
323
+ or any(character not in "0123456789abcdef" for character in revision)
324
+ ):
325
+ raise ValidationError(
326
+ f"environment {revision_environment} must be a full lowercase Git object ID"
327
+ )
328
+ existing = result.get(checkout_environment)
329
+ if existing is not None and existing != revision:
330
+ raise ValidationError(
331
+ f"dynamic checkout expectation for {checkout_environment} "
332
+ "conflicts with the manifest-pinned revision"
333
+ )
334
+ result[checkout_environment] = revision
335
+ return result
336
+
337
+
338
+ def _git_checkout_identity(
339
+ path: Path,
340
+ *,
341
+ expected_revision: str | None,
342
+ ) -> Mapping[str, Any]:
343
+ if shutil.which("git") is None:
344
+ raise ArtifactError("git is required to attest checkout prerequisites")
345
+ top_level = Path(_git(path, "rev-parse", "--show-toplevel")).resolve()
346
+ if top_level != path:
347
+ raise ArtifactError("checkout prerequisite must identify the Git repository root")
348
+ head = _git(path, "rev-parse", "HEAD")
349
+ tree = _git(path, "rev-parse", "HEAD^{tree}")
350
+ for label, value in (("HEAD", head), ("tree", tree)):
351
+ if len(value) not in _GIT_OBJECT_ID_LENGTHS or any(
352
+ character not in "0123456789abcdef" for character in value
353
+ ):
354
+ raise ArtifactError(f"checkout {label} is not a full Git object ID")
355
+ if expected_revision is not None and head != expected_revision:
356
+ raise ArtifactError("checkout HEAD differs from the manifest-pinned revision")
357
+ status = _git(path, "status", "--porcelain=v1", "--untracked-files=all")
358
+ if status:
359
+ raise ArtifactError("checkout prerequisite must have a clean working tree")
360
+ return freeze_mapping(
361
+ {
362
+ "identity_mode": "clean_git_checkout",
363
+ "resolved_path_sha256": _sha256_text(str(path)),
364
+ "git_head": head,
365
+ "git_tree": tree,
366
+ "git_clean": True,
367
+ "expected_revision": expected_revision,
368
+ }
369
+ )
370
+
371
+
372
+ def _git(path: Path, *arguments: str) -> str:
373
+ try:
374
+ completed = subprocess.run(
375
+ ("git", "-C", str(path), *arguments),
376
+ check=True,
377
+ capture_output=True,
378
+ text=True,
379
+ timeout=30,
380
+ )
381
+ except (OSError, subprocess.SubprocessError) as exc:
382
+ raise ArtifactError(f"cannot attest Git checkout with {' '.join(arguments)}") from exc
383
+ return completed.stdout.strip()
384
+
385
+
386
+ def _sha256_directory_tree(root: Path) -> str:
387
+ digest = hashlib.sha256()
388
+ for candidate in sorted(root.rglob("*"), key=lambda item: item.relative_to(root).as_posix()):
389
+ relative = candidate.relative_to(root).as_posix()
390
+ mode = candidate.lstat().st_mode
391
+ if stat.S_ISDIR(mode):
392
+ digest.update(f"D\0{relative}\0".encode())
393
+ elif stat.S_ISREG(mode):
394
+ digest.update(f"F\0{relative}\0".encode())
395
+ digest.update(_sha256_file(candidate).encode())
396
+ digest.update(b"\0")
397
+ elif stat.S_ISLNK(mode):
398
+ target = candidate.resolve(strict=True)
399
+ if not target.is_file():
400
+ raise ArtifactError(f"directory binding symlink must resolve to a file: {relative}")
401
+ digest.update(f"L\0{relative}\0".encode())
402
+ digest.update(_sha256_text(os.readlink(candidate)).encode())
403
+ digest.update(b"\0")
404
+ digest.update(_sha256_file(target).encode())
405
+ digest.update(b"\0")
406
+ else:
407
+ raise ArtifactError(
408
+ f"directory binding contains unsupported filesystem entry: {relative}"
409
+ )
410
+ return digest.hexdigest()
411
+
412
+
413
+ def _sha256_file(path: Path) -> str:
414
+ digest = hashlib.sha256()
415
+ with path.open("rb") as handle:
416
+ for block in iter(lambda: handle.read(1024 * 1024), b""):
417
+ digest.update(block)
418
+ return digest.hexdigest()
419
+
420
+
421
+ def _sha256_text(value: str) -> str:
422
+ return hashlib.sha256(value.encode("utf-8")).hexdigest()
423
+
424
+
425
+ __all__ = [
426
+ "FrozenBindingEntry",
427
+ "FrozenBindingReceipt",
428
+ "build_guarded_driver_fingerprint",
429
+ "resolve_frozen_binding_receipt",
430
+ ]