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,3739 @@
1
+ """Production component builders for static cells and official external arenas.
2
+
3
+ The static experiment driver deliberately accepts operator-owned builders
4
+ instead of guessing deployments. This module provides a strict, reusable
5
+ implementation of the common production builders:
6
+
7
+ * :class:`ProductionBackendBuilder` creates a checksum-bound Replay,
8
+ OpenAI-compatible, or local Transformers backend. It may also attach a
9
+ benchmark-keyed set of checksum-bound retrievers or one checksum-bound
10
+ dense embedder/vector-search runtime for acquisition methods such as HyDE.
11
+ * :class:`PreparedCorpusRetrieverBuilder` builds BM25 or dense retrieval over
12
+ an explicit normalized corpus and checksum-locked local model assets.
13
+ * :class:`ProductionPredictorBuilder` creates the evaluator-blind downstream
14
+ predictor used after an intervention. Retrieval cells emit ranked document
15
+ scores; QA cells generate a strict answer/support/evidence JSON object from
16
+ visible state only.
17
+
18
+ Construction and preflight never contact a model endpoint or download model
19
+ weights. Secrets are accepted only through environment-variable names.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import hashlib
25
+ import importlib
26
+ import importlib.util
27
+ import json
28
+ import math
29
+ import os
30
+ import re
31
+ import stat
32
+ import urllib.parse
33
+ from collections.abc import Mapping, Sequence
34
+ from dataclasses import dataclass, field
35
+ from functools import lru_cache
36
+ from pathlib import Path
37
+ from types import MappingProxyType
38
+ from typing import Any, Protocol, runtime_checkable
39
+
40
+ from agentinputlab.backends.llm import (
41
+ GenerationRequest,
42
+ GenerationResponse,
43
+ LLMBackend,
44
+ ModelCapability,
45
+ OpenAICompatibleBackend,
46
+ ReplayBackend,
47
+ TransformersBackend,
48
+ Usage,
49
+ require_capabilities,
50
+ )
51
+ from agentinputlab.backends.retrieval import (
52
+ Document,
53
+ RetrievalResult,
54
+ Retriever,
55
+ build_retriever,
56
+ retriever_profile,
57
+ )
58
+ from agentinputlab.backends.vector import DenseEmbedder, VectorSearcher
59
+ from agentinputlab.benchmarks import (
60
+ DownstreamPrediction,
61
+ QAPrediction,
62
+ RetrievalPrediction,
63
+ )
64
+ from agentinputlab.connectors.tools import SchemaOnlyToolConnector
65
+ from agentinputlab.contracts.errors import (
66
+ ArtifactError,
67
+ CapabilityError,
68
+ ExternalDependencyError,
69
+ ValidationError,
70
+ )
71
+ from agentinputlab.contracts.serialization import (
72
+ canonical_json,
73
+ content_hash,
74
+ freeze_mapping,
75
+ to_jsonable,
76
+ )
77
+ from agentinputlab.contracts.state import FrozenAgentState
78
+ from agentinputlab.experiments.drivers.static_production import (
79
+ StaticBackendBuildRequest,
80
+ StaticPredictorBuildRequest,
81
+ StaticRuntimeComponents,
82
+ static_predictor_product_sha256,
83
+ static_runtime_product_sha256,
84
+ )
85
+ from agentinputlab.experiments.loader import resolve_driver_entrypoint
86
+ from agentinputlab.observability import ResourceUsage
87
+
88
+ _SCHEMA_VERSION = 1
89
+ _BACKEND_IMPLEMENTATION = "agentinputlab.production_backend_builder.v4"
90
+ _PREDICTOR_IMPLEMENTATION = "agentinputlab.production_downstream_predictor.v7"
91
+ _CORPUS_RETRIEVER_IMPLEMENTATION = "agentinputlab.prepared_corpus_retriever_builder.v1"
92
+ _RETRIEVER_ROUTER_IMPLEMENTATION = "agentinputlab.production_retriever_router.v2"
93
+ _TRANSFORMERS_IMPLEMENTATION = "agentinputlab.local_only_transformers.v2"
94
+ _QUERYGYM_RUNTIME_IMPLEMENTATION = "agentinputlab.querygym.pinned_runtime.v1"
95
+ _RECOMP_RUNTIME_IMPLEMENTATION = "agentinputlab.recomp.current_transformers_loader.v1"
96
+ _MAX_REPLAY_BYTES = 64 * 1024 * 1024
97
+ _MAX_CORPUS_LINE_BYTES = 16 * 1024 * 1024
98
+ _SHA256 = re.compile(r"^[0-9a-f]{64}$")
99
+ _ENTRYPOINT = re.compile(r"^[A-Za-z_][A-Za-z0-9_.]*:[A-Za-z_][A-Za-z0-9_.]*$")
100
+ _ENV_NAME = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
101
+ _SENSITIVE_KEY = re.compile(
102
+ r"(?i)(?:^|[_-])(?:api[_-]?key|authorization|password|secret|"
103
+ r"access[_-]?token|bearer|credential)(?:$|[_-])"
104
+ )
105
+ _SECRET_VALUE_PATTERNS = (
106
+ re.compile(r"(?i)\bbearer\s+[A-Za-z0-9._~+/=-]{8,}"),
107
+ re.compile(r"\bsk-[A-Za-z0-9_-]{8,}"),
108
+ re.compile(r"\bAIza[A-Za-z0-9_-]{20,}\b"),
109
+ re.compile(r"\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b"),
110
+ )
111
+ _SENSITIVE_HEADER = re.compile(
112
+ r"(?i)^(?:authorization|proxy-authorization|x-api-key|api-key|"
113
+ r"x-goog-api-key|x-auth-token|x-amz-security-token|cookie|set-cookie)$"
114
+ )
115
+
116
+ _CELL_SPLITS = MappingProxyType(
117
+ {
118
+ "beir.arguana": "test",
119
+ "bright.earth_science": "test",
120
+ "bright.economics": "test",
121
+ "musique.answerable": "dev",
122
+ "2wiki.multihop": "dev",
123
+ "hotpotqa.distractor": "validation",
124
+ "qasper.v0_3_dev": "dev",
125
+ }
126
+ )
127
+ _RETRIEVAL_CELLS = frozenset(
128
+ {
129
+ "beir.arguana",
130
+ "bright.earth_science",
131
+ "bright.economics",
132
+ }
133
+ )
134
+ _QA_CELLS = frozenset(set(_CELL_SPLITS) - _RETRIEVAL_CELLS)
135
+ _INTERACTIVE_RETRIEVER_CELLS = frozenset(
136
+ {
137
+ "tau3.banking_knowledge",
138
+ "tau3.retail",
139
+ "state_bench.common_support",
140
+ "toolsandbox.common_support",
141
+ "tooltalk.full",
142
+ }
143
+ )
144
+ _RETRIEVER_ROUTE_CELLS = frozenset({*_CELL_SPLITS, *_INTERACTIVE_RETRIEVER_CELLS})
145
+
146
+
147
+ @dataclass(frozen=True, slots=True)
148
+ class ProductionRetrieverBuildRequest:
149
+ """Typed request passed to an operator or built-in retriever builder."""
150
+
151
+ benchmark_id: str
152
+ model_slot: str
153
+ seed: int
154
+ builder_parameters: Mapping[str, Any]
155
+
156
+ def __post_init__(self) -> None:
157
+ _require_text(self.benchmark_id, label="retriever benchmark_id")
158
+ _require_text(self.model_slot, label="model_slot")
159
+ _require_seed(self.seed)
160
+ object.__setattr__(
161
+ self,
162
+ "builder_parameters",
163
+ _canonical_mapping(self.builder_parameters, label="retriever builder parameters"),
164
+ )
165
+
166
+
167
+ @runtime_checkable
168
+ class ProductionRetrieverBuilderProtocol(Protocol):
169
+ """Builder seam used by :class:`ProductionRetrieverRouter`."""
170
+
171
+ builder_kind: str
172
+
173
+ def fingerprint(self) -> Mapping[str, Any]: ...
174
+
175
+ def preflight(self, parameters: Mapping[str, Any]) -> Sequence[str]: ...
176
+
177
+ def build(self, request: ProductionRetrieverBuildRequest) -> Retriever: ...
178
+
179
+
180
+ @dataclass(frozen=True, slots=True)
181
+ class LockedRetrieverSpec:
182
+ """One retriever builder and its exact expected runtime product."""
183
+
184
+ entrypoint: str
185
+ fingerprint_sha256: str
186
+ expected_product_sha256: str
187
+ parameters: Mapping[str, Any] = field(default_factory=dict)
188
+
189
+ def __post_init__(self) -> None:
190
+ if not isinstance(self.entrypoint, str) or _ENTRYPOINT.fullmatch(self.entrypoint) is None:
191
+ raise ValidationError("retriever entrypoint must use module:factory syntax")
192
+ _require_sha256(self.fingerprint_sha256, label="retriever fingerprint_sha256")
193
+ _require_sha256(
194
+ self.expected_product_sha256,
195
+ label="retriever expected_product_sha256",
196
+ )
197
+ _reject_embedded_secrets(self.parameters, path="retriever parameters")
198
+ object.__setattr__(
199
+ self,
200
+ "parameters",
201
+ _canonical_mapping(self.parameters, label="retriever parameters"),
202
+ )
203
+
204
+ @classmethod
205
+ def from_mapping(cls, value: Any, *, path: str) -> LockedRetrieverSpec:
206
+ data = _strict_mapping(
207
+ value,
208
+ path=path,
209
+ allowed={
210
+ "entrypoint",
211
+ "fingerprint_sha256",
212
+ "expected_product_sha256",
213
+ "parameters",
214
+ },
215
+ required={
216
+ "entrypoint",
217
+ "fingerprint_sha256",
218
+ "expected_product_sha256",
219
+ },
220
+ )
221
+ return cls(
222
+ entrypoint=_require_text(data["entrypoint"], label=f"{path}.entrypoint"),
223
+ fingerprint_sha256=_require_text(
224
+ data["fingerprint_sha256"],
225
+ label=f"{path}.fingerprint_sha256",
226
+ ),
227
+ expected_product_sha256=_require_text(
228
+ data["expected_product_sha256"],
229
+ label=f"{path}.expected_product_sha256",
230
+ ),
231
+ parameters=_mapping_or_empty(data.get("parameters"), path=f"{path}.parameters"),
232
+ )
233
+
234
+ @property
235
+ def binding_identity(self) -> Mapping[str, Any]:
236
+ return freeze_mapping(
237
+ {
238
+ "entrypoint": self.entrypoint,
239
+ "fingerprint_sha256": self.fingerprint_sha256,
240
+ "expected_product_sha256": self.expected_product_sha256,
241
+ "parameters_sha256": content_hash(self.parameters),
242
+ }
243
+ )
244
+
245
+
246
+ class _BoundLLMBackend:
247
+ """Add an immutable operator binding without exposing secret values."""
248
+
249
+ __slots__ = ("_binding", "_inner")
250
+
251
+ def __init__(self, inner: LLMBackend, binding: Mapping[str, Any]) -> None:
252
+ if not isinstance(inner, LLMBackend):
253
+ raise TypeError("bound backend requires an LLMBackend")
254
+ self._inner = inner
255
+ self._binding = _canonical_mapping(binding, label="backend binding")
256
+
257
+ @property
258
+ def model_id(self) -> str:
259
+ return self._inner.model_id
260
+
261
+ @property
262
+ def capabilities(self) -> frozenset[ModelCapability]:
263
+ return self._inner.capabilities
264
+
265
+ def generate(self, request: GenerationRequest) -> GenerationResponse:
266
+ return self._inner.generate(request)
267
+
268
+ def fingerprint(self) -> Mapping[str, Any]:
269
+ return {
270
+ "implementation": "agentinputlab.bound_llm_backend.v1",
271
+ "backend": _fingerprint(self._inner, label="inner backend"),
272
+ "binding": self._binding,
273
+ }
274
+
275
+
276
+ class _LocalOnlyTransformersBackend(TransformersBackend):
277
+ """Transformers backend bound to one complete local snapshot tree."""
278
+
279
+ __slots__ = (
280
+ "_artifact_files",
281
+ "_model_path",
282
+ "_snapshot_sha256",
283
+ "local_files_only",
284
+ )
285
+
286
+ def __init__(
287
+ self,
288
+ *,
289
+ model_id: str,
290
+ model_path: Path,
291
+ model_revision: str,
292
+ tokenizer_revision: str,
293
+ artifact_files: tuple[Mapping[str, Any], ...],
294
+ generation_kwargs: Mapping[str, Any],
295
+ capabilities: frozenset[ModelCapability],
296
+ local_files_only: bool,
297
+ ) -> None:
298
+ super().__init__(
299
+ model_id=model_id,
300
+ model_revision=model_revision,
301
+ tokenizer_revision=tokenizer_revision,
302
+ generation_kwargs=generation_kwargs,
303
+ capabilities=capabilities,
304
+ )
305
+ if not isinstance(local_files_only, bool):
306
+ raise ValidationError("transformers local_files_only must be boolean")
307
+ if not isinstance(model_path, Path) or not model_path.is_absolute():
308
+ raise ValidationError("transformers model_path must be an absolute Path")
309
+ self._model_path = model_path
310
+ self._artifact_files = artifact_files
311
+ self._snapshot_sha256 = _transformers_snapshot_sha256(artifact_files)
312
+ self.local_files_only = local_files_only
313
+
314
+ def _load(self) -> None:
315
+ if self.model is not None and self.tokenizer is not None:
316
+ return
317
+ _verify_transformers_snapshot(
318
+ self._model_path,
319
+ self._artifact_files,
320
+ )
321
+ try:
322
+ transformers = importlib.import_module("transformers")
323
+ except ImportError as exc:
324
+ raise ExternalDependencyError(
325
+ "Transformers backend requires `pip install agentinputlab[local]`"
326
+ ) from exc
327
+ tokenizer_factory = getattr(transformers, "AutoTokenizer", None)
328
+ model_factory = getattr(transformers, "AutoModelForCausalLM", None)
329
+ if tokenizer_factory is None or model_factory is None:
330
+ raise ExternalDependencyError(
331
+ "transformers does not expose AutoTokenizer and AutoModelForCausalLM"
332
+ )
333
+ try:
334
+ self.tokenizer = tokenizer_factory.from_pretrained(
335
+ str(self._model_path),
336
+ revision=self.tokenizer_revision,
337
+ trust_remote_code=False,
338
+ local_files_only=self.local_files_only,
339
+ )
340
+ self.model = model_factory.from_pretrained(
341
+ str(self._model_path),
342
+ revision=self.model_revision,
343
+ trust_remote_code=False,
344
+ local_files_only=self.local_files_only,
345
+ )
346
+ except Exception as exc:
347
+ raise ExternalDependencyError(
348
+ "the checksum-bound Transformers deployment is unavailable locally"
349
+ ) from exc
350
+
351
+ def fingerprint(self) -> Mapping[str, Any]:
352
+ return {
353
+ **dict(super().fingerprint()),
354
+ "implementation": _TRANSFORMERS_IMPLEMENTATION,
355
+ "local_files_only": self.local_files_only,
356
+ "snapshot_sha256": self._snapshot_sha256,
357
+ "trust_remote_code": False,
358
+ }
359
+
360
+
361
+ class ProductionRetrieverRouter:
362
+ """Lazy, benchmark-keyed retriever with an identity stable across binding.
363
+
364
+ ``StaticBackendBuildRequest`` intentionally contains a model slot but not a
365
+ benchmark ID. The predictor builder therefore binds this router exactly
366
+ once using the driver-validated benchmark ID. The fingerprint covers all
367
+ available routes and expected products and does not change after binding.
368
+ """
369
+
370
+ retriever_id = "production-retriever-router"
371
+
372
+ def __init__(
373
+ self,
374
+ specs: Mapping[str, LockedRetrieverSpec],
375
+ *,
376
+ model_slot: str,
377
+ seed: int,
378
+ ) -> None:
379
+ routes = dict(specs)
380
+ if not routes:
381
+ raise ValidationError("production retriever router requires at least one route")
382
+ for benchmark_id, spec in routes.items():
383
+ _require_retriever_cell(benchmark_id)
384
+ if not isinstance(spec, LockedRetrieverSpec):
385
+ raise TypeError("retriever route must contain LockedRetrieverSpec values")
386
+ _require_text(model_slot, label="model_slot")
387
+ _require_seed(seed)
388
+ self._specs = MappingProxyType(routes)
389
+ self._model_slot = model_slot
390
+ self._seed = seed
391
+ self._active_benchmark_id: str | None = None
392
+ self._instances: dict[str, Retriever] = {}
393
+
394
+ @property
395
+ def route_ids(self) -> tuple[str, ...]:
396
+ return tuple(sorted(self._specs))
397
+
398
+ def has_route(self, benchmark_id: str) -> bool:
399
+ return benchmark_id in self._specs
400
+
401
+ def bind(self, benchmark_id: str) -> None:
402
+ _require_retriever_cell(benchmark_id)
403
+ if benchmark_id not in self._specs:
404
+ raise CapabilityError(
405
+ f"runtime has no checksum-bound retriever route for {benchmark_id!r}"
406
+ )
407
+ if self._active_benchmark_id is not None and self._active_benchmark_id != benchmark_id:
408
+ raise ValidationError("one runtime retriever router cannot be rebound to another cell")
409
+ self._active_benchmark_id = benchmark_id
410
+
411
+ def retrieve(self, query: str, *, top_k: int = 5) -> tuple[RetrievalResult, ...]:
412
+ if self._active_benchmark_id is None:
413
+ raise CapabilityError(
414
+ "production retriever route is unbound; build a benchmark predictor first"
415
+ )
416
+ retriever = self._retriever_for(self._active_benchmark_id)
417
+ return retriever.retrieve(query, top_k=top_k)
418
+
419
+ def fingerprint(self) -> Mapping[str, Any]:
420
+ return {
421
+ "implementation": _RETRIEVER_ROUTER_IMPLEMENTATION,
422
+ "retriever_id": self.retriever_id,
423
+ "binding_policy": "driver_validated_benchmark_id.v1",
424
+ "routes": {
425
+ benchmark_id: spec.binding_identity
426
+ for benchmark_id, spec in sorted(self._specs.items())
427
+ },
428
+ }
429
+
430
+ def _retriever_for(self, benchmark_id: str) -> Retriever:
431
+ cached = self._instances.get(benchmark_id)
432
+ if cached is not None:
433
+ return cached
434
+ spec = self._specs[benchmark_id]
435
+ builder = _resolve_retriever_builder(spec)
436
+ product = builder.build(
437
+ ProductionRetrieverBuildRequest(
438
+ benchmark_id=benchmark_id,
439
+ model_slot=self._model_slot,
440
+ seed=self._seed,
441
+ builder_parameters=spec.parameters,
442
+ )
443
+ )
444
+ if not isinstance(product, Retriever):
445
+ raise ValidationError(
446
+ "production retriever builder must return a Retriever, "
447
+ f"got {type(product).__name__}"
448
+ )
449
+ observed = retriever_product_sha256(product)
450
+ _validate_product_hash(
451
+ observed,
452
+ spec.expected_product_sha256,
453
+ label=f"retriever route {benchmark_id}",
454
+ )
455
+ self._instances[benchmark_id] = product
456
+ return product
457
+
458
+
459
+ class _BoundRetriever:
460
+ """Bind local corpus/model receipts to a built-in retriever fingerprint."""
461
+
462
+ __slots__ = ("_binding", "_inner")
463
+
464
+ def __init__(self, inner: Retriever, binding: Mapping[str, Any]) -> None:
465
+ if not isinstance(inner, Retriever):
466
+ raise TypeError("bound retriever requires a Retriever")
467
+ self._inner = inner
468
+ self._binding = _canonical_mapping(binding, label="retriever binding")
469
+
470
+ @property
471
+ def retriever_id(self) -> str:
472
+ return self._inner.retriever_id
473
+
474
+ def retrieve(self, query: str, *, top_k: int = 5) -> tuple[RetrievalResult, ...]:
475
+ return self._inner.retrieve(query, top_k=top_k)
476
+
477
+ def fingerprint(self) -> Mapping[str, Any]:
478
+ return {
479
+ "implementation": "agentinputlab.bound_retriever.v1",
480
+ "retriever": _fingerprint(self._inner, label="inner retriever"),
481
+ "binding": self._binding,
482
+ }
483
+
484
+
485
+ class _BoundVectorRuntime(_BoundRetriever):
486
+ """Bind one dense embedder/index pair to corpus and model asset receipts."""
487
+
488
+ def __init__(self, inner: Retriever, binding: Mapping[str, Any]) -> None:
489
+ super().__init__(inner, binding)
490
+ if not isinstance(inner, DenseEmbedder) or not isinstance(inner, VectorSearcher):
491
+ raise TypeError("bound vector runtime requires dense embedder and searcher protocols")
492
+
493
+ @property
494
+ def embedder_id(self) -> str:
495
+ return self._inner.embedder_id
496
+
497
+ @property
498
+ def searcher_id(self) -> str:
499
+ return self._inner.searcher_id
500
+
501
+ def embed(self, texts: Sequence[str]) -> Sequence[Sequence[float]]:
502
+ return self._inner.embed(texts)
503
+
504
+ def search(
505
+ self,
506
+ vector: Sequence[float],
507
+ *,
508
+ top_k: int,
509
+ ) -> Sequence[RetrievalResult]:
510
+ return self._inner.search(vector, top_k=top_k)
511
+
512
+
513
+ class ProductionBackendBuilder:
514
+ """Strict backend/runtime builder used by static production experiments."""
515
+
516
+ builder_kind = "backend"
517
+
518
+ def fingerprint(self) -> Mapping[str, Any]:
519
+ return {
520
+ "implementation": _BACKEND_IMPLEMENTATION,
521
+ "schema_version": _SCHEMA_VERSION,
522
+ "backend_kinds": ("openai_compatible", "replay", "transformers"),
523
+ "tool_connector_kinds": ("schema_only",),
524
+ "external_runtime_kinds": (
525
+ "querygym_query2doc",
526
+ "qrag_common_support",
527
+ "r2ag_common_support",
528
+ "ask_or_assume_worker",
529
+ "acon_observation_worker",
530
+ "reformer_worker",
531
+ "infocqr_worker",
532
+ "recomp_extractive_worker",
533
+ "smartsearch_worker",
534
+ ),
535
+ "secret_policy": "environment_indirection_only.v1",
536
+ "network_policy": "no_preflight_or_construction_network.v1",
537
+ "retriever_binding": _RETRIEVER_ROUTER_IMPLEMENTATION,
538
+ "vector_runtime_binding": "checksum_locked_dense_embedder_and_index.v1",
539
+ "recomp_compatibility_loader": _RECOMP_RUNTIME_IMPLEMENTATION,
540
+ }
541
+
542
+ def preflight(self, parameters: Mapping[str, Any]) -> Sequence[str]:
543
+ try:
544
+ config = _parse_backend_parameters(parameters)
545
+ except (ArtifactError, TypeError, ValueError) as exc:
546
+ return (f"invalid production backend configuration: {exc}",)
547
+ return tuple(_backend_preflight_blockers(config))
548
+
549
+ def build(self, request: StaticBackendBuildRequest) -> StaticRuntimeComponents:
550
+ if not isinstance(request, StaticBackendBuildRequest):
551
+ raise TypeError("production backend builder requires StaticBackendBuildRequest")
552
+ config = _parse_backend_parameters(request.builder_parameters)
553
+ blockers = _backend_preflight_blockers(config)
554
+ if blockers:
555
+ raise ArtifactError("production backend preflight failed: " + "; ".join(blockers))
556
+ backend = _build_llm_backend(config["backend"])
557
+ required = config["required_capabilities"]
558
+ require_capabilities(backend, required)
559
+ external_runtime = _build_external_runtime(config["external_runtime"], backend)
560
+ retriever_specs = config["retrievers"]
561
+ retriever = (
562
+ ProductionRetrieverRouter(
563
+ retriever_specs,
564
+ model_slot=request.model_slot,
565
+ seed=request.seed,
566
+ )
567
+ if retriever_specs
568
+ else None
569
+ )
570
+ vector_runtime = _build_locked_vector_runtime(
571
+ config["vector_runtime"],
572
+ model_slot=request.model_slot,
573
+ seed=request.seed,
574
+ )
575
+ tools = _build_tool_connector(config["tools"])
576
+ return StaticRuntimeComponents(
577
+ backend=backend,
578
+ dense_embedder=vector_runtime,
579
+ retriever=retriever,
580
+ vector_searcher=vector_runtime,
581
+ tools=tools,
582
+ external_runtime=external_runtime,
583
+ run_config=config["run_config"],
584
+ strict_intent=config["strict_intent"],
585
+ )
586
+
587
+ def product_sha256(self, request: StaticBackendBuildRequest) -> str:
588
+ """Build, validate, and hash the exact runtime product."""
589
+
590
+ return static_runtime_product_sha256(self.build(request))
591
+
592
+
593
+ def _build_locked_vector_runtime(
594
+ spec: LockedRetrieverSpec | None,
595
+ *,
596
+ model_slot: str,
597
+ seed: int,
598
+ ) -> DenseEmbedder | VectorSearcher | None:
599
+ if spec is None:
600
+ return None
601
+ benchmark_id = _require_text(
602
+ spec.parameters.get("benchmark_id"),
603
+ label="vector runtime benchmark_id",
604
+ )
605
+ builder = _resolve_retriever_builder(spec)
606
+ product = builder.build(
607
+ ProductionRetrieverBuildRequest(
608
+ benchmark_id=benchmark_id,
609
+ model_slot=model_slot,
610
+ seed=seed,
611
+ builder_parameters=spec.parameters,
612
+ )
613
+ )
614
+ if not isinstance(product, DenseEmbedder) or not isinstance(product, VectorSearcher):
615
+ raise ValidationError(
616
+ "production vector runtime must implement DenseEmbedder and VectorSearcher"
617
+ )
618
+ observed = retriever_product_sha256(product)
619
+ _validate_product_hash(observed, spec.expected_product_sha256, label="vector runtime")
620
+ return product
621
+
622
+
623
+ def _build_tool_connector(config: Mapping[str, Any] | None) -> SchemaOnlyToolConnector | None:
624
+ if config is None:
625
+ return None
626
+ if config["kind"] != "schema_only": # pragma: no cover - parser invariant
627
+ raise ValidationError("unsupported production tool connector kind")
628
+ return SchemaOnlyToolConnector.from_openai_definitions(
629
+ config["definitions"],
630
+ connector_id=config["connector_id"],
631
+ )
632
+
633
+
634
+ class PreparedCorpusRetrieverBuilder:
635
+ """Build BM25/BGE/E5/Qwen/Contriever over one locked normalized corpus."""
636
+
637
+ builder_kind = "retriever"
638
+
639
+ def fingerprint(self) -> Mapping[str, Any]:
640
+ return {
641
+ "implementation": _CORPUS_RETRIEVER_IMPLEMENTATION,
642
+ "schema_version": _SCHEMA_VERSION,
643
+ "corpus_schema": "id-title-text.jsonl.v1",
644
+ "network_policy": "local_files_only.v1",
645
+ "retriever_factory": "agentinputlab.build_retriever.v1",
646
+ }
647
+
648
+ def preflight(self, parameters: Mapping[str, Any]) -> Sequence[str]:
649
+ try:
650
+ config = _parse_corpus_retriever_parameters(parameters)
651
+ except (ArtifactError, TypeError, ValueError) as exc:
652
+ return (f"invalid prepared corpus retriever configuration: {exc}",)
653
+ blockers: list[str] = []
654
+ try:
655
+ documents = _load_locked_corpus(config)
656
+ if len(documents) != config["expected_document_count"]:
657
+ blockers.append(
658
+ "prepared corpus document count mismatch: "
659
+ f"expected {config['expected_document_count']}, got {len(documents)}"
660
+ )
661
+ except (ArtifactError, OSError, UnicodeError, ValueError) as exc:
662
+ blockers.append(str(exc))
663
+ profile = retriever_profile(config["profile_id"])
664
+ if profile.backend == "sentence_transformers_dense":
665
+ if importlib.util.find_spec("sentence_transformers") is None:
666
+ blockers.append(
667
+ "sentence-transformers is unavailable; install agentinputlab[dense-retrieval]"
668
+ )
669
+ if not config["artifact_files"]:
670
+ blockers.append(
671
+ "dense production retrieval requires checksum-locked artifact_files"
672
+ )
673
+ for artifact in config["artifact_files"]:
674
+ try:
675
+ _verify_checksum_file(
676
+ artifact["path"],
677
+ artifact["sha256"],
678
+ label="retriever artifact",
679
+ )
680
+ except ArtifactError as exc:
681
+ blockers.append(str(exc))
682
+ return tuple(blockers)
683
+
684
+ def build(self, request: ProductionRetrieverBuildRequest) -> Retriever:
685
+ if not isinstance(request, ProductionRetrieverBuildRequest):
686
+ raise TypeError(
687
+ "prepared corpus retriever builder requires ProductionRetrieverBuildRequest"
688
+ )
689
+ config = _parse_corpus_retriever_parameters(request.builder_parameters)
690
+ if config["benchmark_id"] != request.benchmark_id:
691
+ raise ValidationError(
692
+ "prepared corpus retriever benchmark differs from the requested route"
693
+ )
694
+ blockers = self.preflight(request.builder_parameters)
695
+ if blockers:
696
+ raise ArtifactError(
697
+ "prepared corpus retriever preflight failed: " + "; ".join(blockers)
698
+ )
699
+ documents = _load_locked_corpus(config)
700
+ retriever = build_retriever(
701
+ config["profile_id"],
702
+ documents,
703
+ **dict(config["options"]),
704
+ )
705
+ bound_type = (
706
+ _BoundVectorRuntime
707
+ if retriever_profile(config["profile_id"]).backend == "sentence_transformers_dense"
708
+ else _BoundRetriever
709
+ )
710
+ return bound_type(
711
+ retriever,
712
+ {
713
+ "builder": _CORPUS_RETRIEVER_IMPLEMENTATION,
714
+ "benchmark_id": request.benchmark_id,
715
+ "profile_id": config["profile_id"],
716
+ "corpus_sha256": config["corpus_sha256"],
717
+ "corpus_revision": config["corpus_revision"],
718
+ "document_count": len(documents),
719
+ "artifact_files": tuple(
720
+ {
721
+ "name": artifact["name"],
722
+ "sha256": artifact["sha256"],
723
+ }
724
+ for artifact in config["artifact_files"]
725
+ ),
726
+ "options": config["options"],
727
+ },
728
+ )
729
+
730
+ def product_sha256(self, request: ProductionRetrieverBuildRequest) -> str:
731
+ return retriever_product_sha256(self.build(request))
732
+
733
+
734
+ class ProductionPredictorBuilder:
735
+ """Build one dataset-aware predictor without exposing evaluator gold."""
736
+
737
+ builder_kind = "predictor"
738
+
739
+ def fingerprint(self) -> Mapping[str, Any]:
740
+ return {
741
+ "implementation": "agentinputlab.production_predictor_builder.v1",
742
+ "schema_version": _SCHEMA_VERSION,
743
+ "predictor_implementation": _PREDICTOR_IMPLEMENTATION,
744
+ "supported_cells": tuple(sorted(_CELL_SPLITS)),
745
+ "gold_boundary": "frozen_agent_state_only.v1",
746
+ "qa_output": (
747
+ "structured_json_object.v1",
748
+ "structured_json_schema.v1",
749
+ "answer_only.v1",
750
+ ),
751
+ "retrieval_output": "ranked_document_scores.v1",
752
+ }
753
+
754
+ def preflight(self, parameters: Mapping[str, Any]) -> Sequence[str]:
755
+ try:
756
+ _parse_predictor_parameters(parameters)
757
+ except (ArtifactError, TypeError, ValueError) as exc:
758
+ return (f"invalid production predictor configuration: {exc}",)
759
+ return ()
760
+
761
+ def build(self, request: StaticPredictorBuildRequest) -> ProductionDownstreamPredictor:
762
+ if not isinstance(request, StaticPredictorBuildRequest):
763
+ raise TypeError("production predictor builder requires StaticPredictorBuildRequest")
764
+ config = _parse_predictor_parameters(request.builder_parameters)
765
+ _require_cell(request.benchmark_id)
766
+ expected_split = _CELL_SPLITS[request.benchmark_id]
767
+ if request.split != expected_split:
768
+ raise ValidationError(
769
+ f"production predictor expects split {expected_split!r} for "
770
+ f"{request.benchmark_id!r}"
771
+ )
772
+ inferred_task = "retrieval" if request.benchmark_id in _RETRIEVAL_CELLS else "qa"
773
+ if config["task"] not in {"auto", inferred_task}:
774
+ raise ValidationError(
775
+ f"predictor task {config['task']!r} is incompatible with {request.benchmark_id!r}"
776
+ )
777
+
778
+ retriever = request.runtime.retriever
779
+ if isinstance(retriever, ProductionRetrieverRouter) and retriever.has_route(
780
+ request.benchmark_id
781
+ ):
782
+ retriever.bind(request.benchmark_id)
783
+ if inferred_task == "retrieval":
784
+ if retriever is None or not isinstance(retriever, Retriever):
785
+ raise CapabilityError(
786
+ f"retrieval cell {request.benchmark_id!r} requires a locked runtime retriever"
787
+ )
788
+ if isinstance(retriever, ProductionRetrieverRouter):
789
+ retriever.bind(request.benchmark_id)
790
+ else:
791
+ backend = request.runtime.backend
792
+ if not isinstance(backend, LLMBackend):
793
+ raise CapabilityError("QA prediction requires an LLMBackend")
794
+ required = set(config["qa"]["required_capabilities"])
795
+ if config["qa"]["use_seed"]:
796
+ required.add(ModelCapability.SEED)
797
+ require_capabilities(backend, required)
798
+
799
+ return ProductionDownstreamPredictor(
800
+ benchmark_id=request.benchmark_id,
801
+ split=request.split,
802
+ seed=request.seed,
803
+ task=inferred_task,
804
+ runtime=request.runtime,
805
+ config=config,
806
+ )
807
+
808
+ def product_sha256(self, request: StaticPredictorBuildRequest) -> str:
809
+ return static_predictor_product_sha256(self.build(request))
810
+
811
+
812
+ class ProductionDownstreamPredictor:
813
+ """Evaluator-blind predictor shared by all seven static cells."""
814
+
815
+ __slots__ = ("_benchmark_id", "_config", "_runtime", "_seed", "_split", "_task")
816
+
817
+ def __init__(
818
+ self,
819
+ *,
820
+ benchmark_id: str,
821
+ split: str,
822
+ seed: int,
823
+ task: str,
824
+ runtime: StaticRuntimeComponents,
825
+ config: Mapping[str, Any],
826
+ ) -> None:
827
+ _require_cell(benchmark_id)
828
+ if split != _CELL_SPLITS[benchmark_id]:
829
+ raise ValidationError("production predictor split differs from the locked cell")
830
+ _require_seed(seed)
831
+ if task not in {"qa", "retrieval"}:
832
+ raise ValidationError("production predictor task must be qa or retrieval")
833
+ if not isinstance(runtime, StaticRuntimeComponents):
834
+ raise TypeError("production predictor requires StaticRuntimeComponents")
835
+ self._benchmark_id = benchmark_id
836
+ self._split = split
837
+ self._seed = seed
838
+ self._task = task
839
+ self._runtime = runtime
840
+ self._config = _canonical_mapping(config, label="predictor configuration")
841
+
842
+ def fingerprint(self) -> Mapping[str, Any]:
843
+ return {
844
+ "implementation": _PREDICTOR_IMPLEMENTATION,
845
+ "benchmark_id": self._benchmark_id,
846
+ "split": self._split,
847
+ "task": self._task,
848
+ "configuration_sha256": content_hash(self._config),
849
+ # The concrete backend/retriever is already locked by the model-slot
850
+ # runtime product. Keeping it out of the predictor recipe allows one
851
+ # predictor lock to be reused fairly across multiple model slots.
852
+ "runtime_binding_slot": "backend" if self._task == "qa" else "retriever",
853
+ "visible_input_schema": "frozen_agent_state_without_task_or_metadata.v1",
854
+ }
855
+
856
+ def predict(self, state: FrozenAgentState) -> DownstreamPrediction:
857
+ if not isinstance(state, FrozenAgentState):
858
+ raise TypeError("production downstream predictor requires FrozenAgentState")
859
+ if self._task == "retrieval":
860
+ return self._predict_retrieval(state)
861
+ return self._predict_qa(state)
862
+
863
+ def _predict_retrieval(self, state: FrozenAgentState) -> DownstreamPrediction:
864
+ options = self._config["retrieval"]
865
+ if options["source_mode"] == "context_or_retriever":
866
+ context_ranking = _retrieval_prediction_from_context(
867
+ state,
868
+ top_k=options["top_k"],
869
+ )
870
+ if context_ranking is not None:
871
+ prediction, document_ids = context_ranking
872
+ return DownstreamPrediction(
873
+ prediction=prediction,
874
+ usage=ResourceUsage(),
875
+ metadata={
876
+ "prediction_kind": "retrieval_visible_context",
877
+ "visible_context_sha256": content_hash(
878
+ tuple(
879
+ {
880
+ "block_id": block.block_id,
881
+ "text": block.text,
882
+ "metadata": block.metadata,
883
+ }
884
+ for block in state.context
885
+ )
886
+ ),
887
+ "returned_documents": len(document_ids),
888
+ "top_k": options["top_k"],
889
+ },
890
+ )
891
+ retriever = self._runtime.retriever
892
+ if not isinstance(retriever, Retriever):
893
+ raise CapabilityError("production retrieval predictor has no runtime retriever")
894
+ if state.budget.max_retrieval_calls < 1:
895
+ raise CapabilityError("retrieval prediction has no remaining retrieval-call budget")
896
+ query = (
897
+ state.query
898
+ if options["query_mode"] == "query_only"
899
+ else "\n".join(part for part in (state.instruction, state.query) if part)
900
+ )
901
+ if not query.strip():
902
+ raise ValidationError("production retrieval query is empty")
903
+ results = tuple(retriever.retrieve(query, top_k=options["top_k"]))
904
+ if any(not isinstance(item, RetrievalResult) for item in results):
905
+ raise ValidationError("runtime retriever returned a non-RetrievalResult")
906
+ document_ids = tuple(item.document.doc_id for item in results)
907
+ if len(document_ids) != len(set(document_ids)):
908
+ raise ValidationError("runtime retriever returned duplicate document IDs")
909
+ expected_ranks = tuple(range(1, len(results) + 1))
910
+ if tuple(item.rank for item in results) != expected_ranks:
911
+ raise ValidationError("runtime retriever ranks must be contiguous from one")
912
+ prediction = RetrievalPrediction({item.document.doc_id: item.score for item in results})
913
+ return DownstreamPrediction(
914
+ prediction=prediction,
915
+ usage=ResourceUsage(retrieval_calls=1),
916
+ metadata={
917
+ "prediction_kind": "retrieval",
918
+ "query_sha256": content_hash(query),
919
+ "returned_documents": len(results),
920
+ "top_k": options["top_k"],
921
+ },
922
+ )
923
+
924
+ def _predict_qa(self, state: FrozenAgentState) -> DownstreamPrediction:
925
+ backend = self._runtime.backend
926
+ if not isinstance(backend, LLMBackend):
927
+ raise CapabilityError("production QA predictor has no LLM backend")
928
+ if state.budget.max_model_calls < 1:
929
+ raise CapabilityError("QA prediction has no remaining model-call budget")
930
+ options = self._config["qa"]
931
+ request_limit = min(options["max_tokens"], state.budget.max_output_tokens)
932
+ if request_limit < 1:
933
+ raise CapabilityError("QA prediction has no remaining output-token budget")
934
+ request = GenerationRequest(
935
+ prompt=_render_visible_qa_prompt(state),
936
+ system=options["system_prompt"],
937
+ temperature=options["temperature"],
938
+ max_tokens=request_limit,
939
+ seed=self._seed if options["use_seed"] else None,
940
+ stop=options["stop"],
941
+ response_format=(
942
+ _qa_response_format(
943
+ benchmark_id=self._benchmark_id,
944
+ output_format=options["output_format"],
945
+ structured_output_mode=options["structured_output_mode"],
946
+ )
947
+ ),
948
+ metadata={
949
+ "benchmark_id_sha256": content_hash(self._benchmark_id),
950
+ "prompt_revision": options["prompt_revision"],
951
+ },
952
+ )
953
+ response = backend.generate(request)
954
+ if not isinstance(response, GenerationResponse):
955
+ raise ValidationError("LLM backend returned a non-GenerationResponse")
956
+ if not isinstance(response.text, str):
957
+ raise ValidationError("LLM backend response text must be a string")
958
+ usage = response.usage
959
+ if not isinstance(usage, Usage):
960
+ raise ValidationError("LLM backend response usage must be Usage")
961
+ cost = usage.cost_usd
962
+ prediction, support_audit = _parse_visible_qa_prediction(
963
+ response.text,
964
+ benchmark_id=self._benchmark_id,
965
+ state=state,
966
+ output_format=options["output_format"],
967
+ identifier_policy=options["identifier_policy"],
968
+ )
969
+ return DownstreamPrediction(
970
+ prediction=prediction,
971
+ usage=ResourceUsage(
972
+ model_calls=1,
973
+ unpriced_model_calls=1 if cost is None else 0,
974
+ input_tokens=usage.input_tokens,
975
+ output_tokens=usage.output_tokens,
976
+ cost_usd=0.0 if cost is None else cost,
977
+ ),
978
+ metadata={
979
+ "prediction_kind": f"qa_{options['output_format']}",
980
+ "request_sha256": request.request_hash,
981
+ "model": response.model,
982
+ "finish_reason": response.finish_reason,
983
+ **support_audit,
984
+ },
985
+ )
986
+
987
+
988
+ def _retrieval_prediction_from_context(
989
+ state: FrozenAgentState,
990
+ *,
991
+ top_k: int,
992
+ ) -> tuple[RetrievalPrediction, tuple[str, ...]] | None:
993
+ """Recover a ranked candidate list from provenance-preserving context.
994
+
995
+ Auxiliary retrieval tasks preload the same candidate set for both arms
996
+ when a curation method is evaluated. Acquisition methods may instead add
997
+ their own retrieved documents. Only visible provenance is consumed.
998
+ """
999
+
1000
+ document_ids: list[str] = []
1001
+ for block in state.context:
1002
+ raw_ids: tuple[str, ...]
1003
+ direct = block.metadata.get("document_id")
1004
+ if not isinstance(direct, str) or not direct.strip():
1005
+ direct = block.metadata.get("source_context_block_id")
1006
+ if isinstance(direct, str) and direct.strip():
1007
+ raw_ids = (direct,)
1008
+ else:
1009
+ lineage = block.metadata.get("source_block_ids")
1010
+ if isinstance(lineage, (tuple, list)) and all(
1011
+ isinstance(item, str) and item.strip() for item in lineage
1012
+ ):
1013
+ raw_ids = tuple(lineage)
1014
+ else:
1015
+ raw_ids = ()
1016
+ for document_id in raw_ids:
1017
+ if document_id not in document_ids:
1018
+ document_ids.append(document_id)
1019
+ if len(document_ids) >= top_k:
1020
+ break
1021
+ if len(document_ids) >= top_k:
1022
+ break
1023
+ if not document_ids:
1024
+ return None
1025
+ size = len(document_ids)
1026
+ return (
1027
+ RetrievalPrediction(
1028
+ {document_id: float(size - rank) for rank, document_id in enumerate(document_ids)}
1029
+ ),
1030
+ tuple(document_ids),
1031
+ )
1032
+
1033
+
1034
+ def production_backend_builder_factory() -> ProductionBackendBuilder:
1035
+ """Zero-argument entrypoint for ``LockedBuilderSpec``."""
1036
+
1037
+ return ProductionBackendBuilder()
1038
+
1039
+
1040
+ def production_predictor_builder_factory() -> ProductionPredictorBuilder:
1041
+ """Zero-argument entrypoint for ``LockedBuilderSpec``."""
1042
+
1043
+ return ProductionPredictorBuilder()
1044
+
1045
+
1046
+ def prepared_corpus_retriever_builder_factory() -> PreparedCorpusRetrieverBuilder:
1047
+ """Zero-argument entrypoint for a nested ``LockedRetrieverSpec``."""
1048
+
1049
+ return PreparedCorpusRetrieverBuilder()
1050
+
1051
+
1052
+ def builder_fingerprint_sha256(builder: Any) -> str:
1053
+ """Return the checksum placed in a locked builder specification."""
1054
+
1055
+ return content_hash(_fingerprint(builder, label="builder"))
1056
+
1057
+
1058
+ def retriever_product_sha256(retriever: Retriever) -> str:
1059
+ """Hash an exact retriever product without performing retrieval."""
1060
+
1061
+ if not isinstance(retriever, Retriever):
1062
+ raise TypeError("retriever product hashing requires a Retriever")
1063
+ return content_hash(_fingerprint(retriever, label="retriever"))
1064
+
1065
+
1066
+ def validate_runtime_product_sha256(
1067
+ runtime: StaticRuntimeComponents,
1068
+ expected_sha256: str,
1069
+ ) -> str:
1070
+ """Validate and return the static driver's exact runtime product hash."""
1071
+
1072
+ observed = static_runtime_product_sha256(runtime)
1073
+ return _validate_product_hash(observed, expected_sha256, label="runtime product")
1074
+
1075
+
1076
+ def validate_predictor_product_sha256(
1077
+ predictor: ProductionDownstreamPredictor,
1078
+ expected_sha256: str,
1079
+ ) -> str:
1080
+ """Validate and return the static driver's exact predictor product hash."""
1081
+
1082
+ observed = static_predictor_product_sha256(predictor)
1083
+ return _validate_product_hash(observed, expected_sha256, label="predictor product")
1084
+
1085
+
1086
+ def validate_retriever_product_sha256(
1087
+ retriever: Retriever,
1088
+ expected_sha256: str,
1089
+ ) -> str:
1090
+ """Validate and return an exact retriever product hash."""
1091
+
1092
+ observed = retriever_product_sha256(retriever)
1093
+ return _validate_product_hash(observed, expected_sha256, label="retriever product")
1094
+
1095
+
1096
+ def _parse_backend_parameters(parameters: Mapping[str, Any]) -> Mapping[str, Any]:
1097
+ _reject_embedded_secrets(parameters, path="backend parameters")
1098
+ data = _strict_mapping(
1099
+ parameters,
1100
+ path="backend parameters",
1101
+ allowed={
1102
+ "schema_version",
1103
+ "backend",
1104
+ "external_runtime",
1105
+ "required_capabilities",
1106
+ "required_env",
1107
+ "retrievers",
1108
+ "vector_runtime",
1109
+ "run_config",
1110
+ "strict_intent",
1111
+ "tools",
1112
+ },
1113
+ required={"schema_version", "backend"},
1114
+ )
1115
+ _require_schema_version(data["schema_version"], path="backend parameters.schema_version")
1116
+ backend = _parse_backend_block(data["backend"])
1117
+ external_runtime = _parse_external_runtime_block(data.get("external_runtime"))
1118
+ required_capabilities = _capabilities(
1119
+ data.get("required_capabilities", (ModelCapability.COMPLETION.value,)),
1120
+ path="backend parameters.required_capabilities",
1121
+ )
1122
+ if not required_capabilities:
1123
+ raise ValidationError("backend required_capabilities must not be empty")
1124
+ retriever_values = _mapping_or_empty(
1125
+ data.get("retrievers"),
1126
+ path="backend parameters.retrievers",
1127
+ )
1128
+ retrievers: dict[str, LockedRetrieverSpec] = {}
1129
+ for benchmark_id, value in retriever_values.items():
1130
+ _require_retriever_cell(benchmark_id)
1131
+ retrievers[benchmark_id] = LockedRetrieverSpec.from_mapping(
1132
+ value,
1133
+ path=f"backend parameters.retrievers.{benchmark_id}",
1134
+ )
1135
+ vector_runtime = None
1136
+ if data.get("vector_runtime") is not None:
1137
+ vector_runtime = LockedRetrieverSpec.from_mapping(
1138
+ data["vector_runtime"],
1139
+ path="backend parameters.vector_runtime",
1140
+ )
1141
+ _require_text(
1142
+ vector_runtime.parameters.get("benchmark_id"),
1143
+ label="vector runtime benchmark_id",
1144
+ )
1145
+ tools = _parse_tool_connector_block(data.get("tools"))
1146
+ required_env = _environment_names(
1147
+ data.get("required_env", ()),
1148
+ path="backend parameters.required_env",
1149
+ )
1150
+ if backend["kind"] == "openai_compatible" and backend["require_api_key"]:
1151
+ required_env = tuple(sorted({*required_env, backend["api_key_env"]}))
1152
+ if external_runtime is not None and external_runtime["kind"] in {
1153
+ "acon_observation_worker",
1154
+ "ask_or_assume_worker",
1155
+ "reformer_worker",
1156
+ "infocqr_worker",
1157
+ }:
1158
+ api_key_env = external_runtime.get("api_key_env")
1159
+ if api_key_env is not None:
1160
+ required_env = tuple(sorted({*required_env, api_key_env}))
1161
+ if backend["kind"] == "openai_compatible" and backend["deployment_revision_env"] is not None:
1162
+ required_env = tuple(sorted({*required_env, backend["deployment_revision_env"]}))
1163
+ strict_intent = data.get("strict_intent", False)
1164
+ if not isinstance(strict_intent, bool):
1165
+ raise ValidationError("backend parameters.strict_intent must be boolean")
1166
+ if strict_intent:
1167
+ raise ValidationError(
1168
+ "ProductionBackendBuilder cannot infer a per-example IntentContract; "
1169
+ "use a custom locked runtime builder that supplies intent_contract, or set "
1170
+ "strict_intent=false and report intent preservation as an evaluation metric"
1171
+ )
1172
+ return MappingProxyType(
1173
+ {
1174
+ "backend": backend,
1175
+ "external_runtime": external_runtime,
1176
+ "required_capabilities": required_capabilities,
1177
+ "required_env": required_env,
1178
+ "retrievers": MappingProxyType(retrievers),
1179
+ "vector_runtime": vector_runtime,
1180
+ "tools": tools,
1181
+ "run_config": _canonical_mapping(
1182
+ _mapping_or_empty(
1183
+ data.get("run_config"),
1184
+ path="backend parameters.run_config",
1185
+ ),
1186
+ label="backend run_config",
1187
+ ),
1188
+ "strict_intent": strict_intent,
1189
+ }
1190
+ )
1191
+
1192
+
1193
+ def _parse_tool_connector_block(value: Any) -> Mapping[str, Any] | None:
1194
+ if value is None:
1195
+ return None
1196
+ data = _strict_mapping(
1197
+ value,
1198
+ path="backend parameters.tools",
1199
+ allowed={"kind", "connector_id", "definitions"},
1200
+ required={"kind", "connector_id", "definitions"},
1201
+ )
1202
+ kind = _require_text(data["kind"], label="tool connector kind")
1203
+ if kind != "schema_only":
1204
+ raise ValidationError("production tool connector kind must be schema_only")
1205
+ connector_id = _require_text(data["connector_id"], label="tool connector ID")
1206
+ definitions = data["definitions"]
1207
+ if isinstance(definitions, (str, bytes)) or not isinstance(definitions, Sequence):
1208
+ raise ValidationError("tool connector definitions must be a sequence")
1209
+ normalized: list[Mapping[str, Any]] = []
1210
+ for index, definition in enumerate(definitions):
1211
+ if not isinstance(definition, Mapping):
1212
+ raise ValidationError(f"tool connector definition {index} must be an object")
1213
+ normalized.append(
1214
+ _canonical_mapping(definition, label=f"tool connector definition {index}")
1215
+ )
1216
+ # Construct once during parsing so malformed OpenAI declarations fail preflight.
1217
+ SchemaOnlyToolConnector.from_openai_definitions(
1218
+ normalized,
1219
+ connector_id=connector_id,
1220
+ )
1221
+ return MappingProxyType(
1222
+ {
1223
+ "kind": kind,
1224
+ "connector_id": connector_id,
1225
+ "definitions": tuple(normalized),
1226
+ }
1227
+ )
1228
+
1229
+
1230
+ def _parse_external_runtime_block(value: Any) -> Mapping[str, Any] | None:
1231
+ if value is None:
1232
+ return None
1233
+ data = _strict_mapping(
1234
+ value,
1235
+ path="backend parameters.external_runtime",
1236
+ allowed={
1237
+ "kind",
1238
+ "checkout",
1239
+ "artifact_root",
1240
+ "manifest_sha256",
1241
+ "source_root",
1242
+ "base_model_root",
1243
+ "base_model_load_set_sha256",
1244
+ "feature_model_root",
1245
+ "feature_model_load_set_sha256",
1246
+ "reader_device",
1247
+ "feature_device",
1248
+ "checkpoint_root",
1249
+ "checkpoint_manifest_path",
1250
+ "checkpoint_manifest_sha256",
1251
+ "runtime_manifest_path",
1252
+ "runtime_manifest_sha256",
1253
+ "embedder_root",
1254
+ "compatibility_manifest_path",
1255
+ "compatibility_manifest_sha256",
1256
+ "embedder_manifest_sha256",
1257
+ "device",
1258
+ "driver_path",
1259
+ "driver_sha256",
1260
+ "python_executable",
1261
+ "python_executable_sha256",
1262
+ "base_url",
1263
+ "api_key_env",
1264
+ "model_id",
1265
+ "optimizer_model_revision",
1266
+ "target_model_revision",
1267
+ "tokenizer_revision",
1268
+ "require_api_key",
1269
+ "extra_body",
1270
+ "timeout_seconds",
1271
+ "max_input_tokens_per_call",
1272
+ "max_output_tokens",
1273
+ "temperature",
1274
+ "input_cost_per_million",
1275
+ "output_cost_per_million",
1276
+ },
1277
+ required={"kind"},
1278
+ )
1279
+ kind = _require_text(data["kind"], label="external runtime kind")
1280
+ if kind == "querygym_query2doc":
1281
+ _require_exact_backend_fields(
1282
+ data,
1283
+ {"kind", "checkout"},
1284
+ required={"kind", "checkout"},
1285
+ kind="QueryGym external runtime",
1286
+ )
1287
+ return MappingProxyType(
1288
+ {
1289
+ "kind": kind,
1290
+ "checkout": _absolute_path(data["checkout"], label="QueryGym checkout"),
1291
+ }
1292
+ )
1293
+ if kind == "recomp_extractive_worker":
1294
+ required = {
1295
+ "kind",
1296
+ "checkout",
1297
+ "checkpoint_root",
1298
+ "checkpoint_manifest_path",
1299
+ "checkpoint_manifest_sha256",
1300
+ "compatibility_manifest_path",
1301
+ "compatibility_manifest_sha256",
1302
+ "device",
1303
+ }
1304
+ _require_exact_backend_fields(
1305
+ data,
1306
+ required,
1307
+ required=required,
1308
+ kind="RECOMP extractive external runtime",
1309
+ )
1310
+ device = _require_text(data["device"], label="RECOMP device")
1311
+ if re.fullmatch(r"cpu|cuda(?::[0-9]+)?", device) is None:
1312
+ raise ValidationError("RECOMP device must be cpu, cuda, or cuda:<index>")
1313
+ return MappingProxyType(
1314
+ {
1315
+ "kind": kind,
1316
+ "checkout": _absolute_path(data["checkout"], label="RECOMP checkout"),
1317
+ "checkpoint_root": _absolute_path(
1318
+ data["checkpoint_root"], label="RECOMP checkpoint root"
1319
+ ),
1320
+ "checkpoint_manifest_path": _absolute_path(
1321
+ data["checkpoint_manifest_path"], label="RECOMP checkpoint manifest"
1322
+ ),
1323
+ "checkpoint_manifest_sha256": _sha256_text(
1324
+ data["checkpoint_manifest_sha256"],
1325
+ label="RECOMP checkpoint manifest SHA-256",
1326
+ ),
1327
+ "compatibility_manifest_path": _absolute_path(
1328
+ data["compatibility_manifest_path"],
1329
+ label="RECOMP compatibility manifest",
1330
+ ),
1331
+ "compatibility_manifest_sha256": _sha256_text(
1332
+ data["compatibility_manifest_sha256"],
1333
+ label="RECOMP compatibility manifest SHA-256",
1334
+ ),
1335
+ "device": device,
1336
+ }
1337
+ )
1338
+ if kind == "acon_observation_worker":
1339
+ required = {
1340
+ "kind",
1341
+ "checkout",
1342
+ "driver_path",
1343
+ "driver_sha256",
1344
+ "python_executable",
1345
+ "python_executable_sha256",
1346
+ "compatibility_manifest_path",
1347
+ "compatibility_manifest_sha256",
1348
+ "base_url",
1349
+ "model_id",
1350
+ "optimizer_model_revision",
1351
+ "target_model_revision",
1352
+ "tokenizer_revision",
1353
+ }
1354
+ optional = {
1355
+ "extra_body",
1356
+ "require_api_key",
1357
+ "timeout_seconds",
1358
+ "max_output_tokens",
1359
+ "temperature",
1360
+ }
1361
+ _require_exact_backend_fields(
1362
+ data,
1363
+ required | optional,
1364
+ required=required,
1365
+ kind="ACON observation external runtime",
1366
+ )
1367
+ driver_path = _absolute_path(data["driver_path"], label="ACON driver")
1368
+ compatibility_manifest_path = _absolute_path(
1369
+ data["compatibility_manifest_path"], label="ACON compatibility manifest"
1370
+ )
1371
+ if compatibility_manifest_path != driver_path.with_name("compatibility_manifest.json"):
1372
+ raise ValidationError(
1373
+ "ACON compatibility manifest must be the worker driver's sibling manifest"
1374
+ )
1375
+ require_api_key = data.get("require_api_key", False)
1376
+ if not isinstance(require_api_key, bool):
1377
+ raise ValidationError("ACON require_api_key must be boolean")
1378
+ temperature = _nonnegative_number(data.get("temperature", 0.0), label="ACON temperature")
1379
+ if temperature > 2:
1380
+ raise ValidationError("ACON temperature must not exceed 2")
1381
+ return MappingProxyType(
1382
+ {
1383
+ "kind": kind,
1384
+ "checkout": _absolute_path(data["checkout"], label="ACON checkout"),
1385
+ "driver_path": driver_path,
1386
+ "driver_sha256": _sha256_text(data["driver_sha256"], label="ACON driver SHA-256"),
1387
+ "python_executable": _absolute_path(
1388
+ data["python_executable"], label="ACON Python executable"
1389
+ ),
1390
+ "python_executable_sha256": _sha256_text(
1391
+ data["python_executable_sha256"], label="ACON Python executable SHA-256"
1392
+ ),
1393
+ "compatibility_manifest_path": compatibility_manifest_path,
1394
+ "compatibility_manifest_sha256": _sha256_text(
1395
+ data["compatibility_manifest_sha256"],
1396
+ label="ACON compatibility manifest SHA-256",
1397
+ ),
1398
+ "base_url": _absolute_http_url(data["base_url"], label="ACON base_url"),
1399
+ "api_key_env": "AGENTINPUTLAB_ACON_API_KEY" if require_api_key else None,
1400
+ "model_id": _require_text(data["model_id"], label="ACON model_id"),
1401
+ "optimizer_model_revision": _require_text(
1402
+ data["optimizer_model_revision"], label="ACON optimizer_model_revision"
1403
+ ),
1404
+ "target_model_revision": _require_text(
1405
+ data["target_model_revision"], label="ACON target_model_revision"
1406
+ ),
1407
+ "tokenizer_revision": _require_text(
1408
+ data["tokenizer_revision"], label="ACON tokenizer_revision"
1409
+ ),
1410
+ "extra_body": _canonical_mapping(
1411
+ _mapping_or_empty(data.get("extra_body"), path="ACON extra_body"),
1412
+ label="ACON extra_body",
1413
+ ),
1414
+ "timeout_seconds": _positive_number(
1415
+ data.get("timeout_seconds", 600.0), label="ACON timeout_seconds"
1416
+ ),
1417
+ "max_output_tokens": _positive_int(
1418
+ data.get("max_output_tokens", 512), label="ACON max_output_tokens"
1419
+ ),
1420
+ "temperature": temperature,
1421
+ }
1422
+ )
1423
+ if kind == "ask_or_assume_worker":
1424
+ required = {
1425
+ "kind",
1426
+ "checkout",
1427
+ "driver_path",
1428
+ "driver_sha256",
1429
+ "python_executable",
1430
+ "python_executable_sha256",
1431
+ "compatibility_manifest_path",
1432
+ "compatibility_manifest_sha256",
1433
+ "base_url",
1434
+ "api_key_env",
1435
+ "model_id",
1436
+ }
1437
+ optional = {
1438
+ "timeout_seconds",
1439
+ "max_output_tokens",
1440
+ "input_cost_per_million",
1441
+ "output_cost_per_million",
1442
+ }
1443
+ _require_exact_backend_fields(
1444
+ data,
1445
+ required | optional,
1446
+ required=required,
1447
+ kind="Ask-or-Assume external runtime",
1448
+ )
1449
+ return MappingProxyType(
1450
+ {
1451
+ "kind": kind,
1452
+ "checkout": _absolute_path(data["checkout"], label="Ask-or-Assume checkout"),
1453
+ "driver_path": _absolute_path(data["driver_path"], label="Ask-or-Assume driver"),
1454
+ "driver_sha256": _sha256_text(
1455
+ data["driver_sha256"], label="Ask-or-Assume driver SHA-256"
1456
+ ),
1457
+ "python_executable": _absolute_path(
1458
+ data["python_executable"], label="Ask-or-Assume Python executable"
1459
+ ),
1460
+ "python_executable_sha256": _sha256_text(
1461
+ data["python_executable_sha256"],
1462
+ label="Ask-or-Assume Python executable SHA-256",
1463
+ ),
1464
+ "compatibility_manifest_path": _absolute_path(
1465
+ data["compatibility_manifest_path"],
1466
+ label="Ask-or-Assume compatibility manifest",
1467
+ ),
1468
+ "compatibility_manifest_sha256": _sha256_text(
1469
+ data["compatibility_manifest_sha256"],
1470
+ label="Ask-or-Assume compatibility manifest SHA-256",
1471
+ ),
1472
+ "base_url": _absolute_http_url(data["base_url"], label="Ask-or-Assume base_url"),
1473
+ "api_key_env": _require_env_name(
1474
+ data["api_key_env"], label="Ask-or-Assume api_key_env"
1475
+ ),
1476
+ "model_id": _require_text(data["model_id"], label="Ask-or-Assume model_id"),
1477
+ "timeout_seconds": _positive_number(
1478
+ data.get("timeout_seconds", 600.0),
1479
+ label="Ask-or-Assume timeout_seconds",
1480
+ ),
1481
+ "max_output_tokens": _positive_int(
1482
+ data.get("max_output_tokens", 512),
1483
+ label="Ask-or-Assume max_output_tokens",
1484
+ ),
1485
+ "input_cost_per_million": _nonnegative_number(
1486
+ data.get("input_cost_per_million", 0.0),
1487
+ label="Ask-or-Assume input_cost_per_million",
1488
+ ),
1489
+ "output_cost_per_million": _nonnegative_number(
1490
+ data.get("output_cost_per_million", 0.0),
1491
+ label="Ask-or-Assume output_cost_per_million",
1492
+ ),
1493
+ }
1494
+ )
1495
+ if kind in {"reformer_worker", "infocqr_worker"}:
1496
+ required = {
1497
+ "kind",
1498
+ "checkout",
1499
+ "driver_path",
1500
+ "driver_sha256",
1501
+ "python_executable",
1502
+ "python_executable_sha256",
1503
+ "compatibility_manifest_path",
1504
+ "compatibility_manifest_sha256",
1505
+ "base_url",
1506
+ "api_key_env",
1507
+ "model_id",
1508
+ }
1509
+ optional = {
1510
+ "extra_body",
1511
+ "timeout_seconds",
1512
+ "max_input_tokens_per_call",
1513
+ "max_output_tokens",
1514
+ "temperature",
1515
+ "input_cost_per_million",
1516
+ "output_cost_per_million",
1517
+ }
1518
+ _require_exact_backend_fields(
1519
+ data, required | optional, required=required, kind="ReFormeR external runtime"
1520
+ )
1521
+ temperature = _nonnegative_number(
1522
+ data.get("temperature", 0.0), label="ReFormeR temperature"
1523
+ )
1524
+ if temperature > 2:
1525
+ raise ValidationError("ReFormeR temperature must not exceed 2")
1526
+ return MappingProxyType(
1527
+ {
1528
+ "kind": kind,
1529
+ "checkout": _absolute_path(data["checkout"], label="ReFormeR checkout"),
1530
+ "driver_path": _absolute_path(data["driver_path"], label="ReFormeR driver"),
1531
+ "driver_sha256": _sha256_text(
1532
+ data["driver_sha256"], label="ReFormeR driver SHA-256"
1533
+ ),
1534
+ "python_executable": _absolute_path(
1535
+ data["python_executable"], label="ReFormeR Python executable"
1536
+ ),
1537
+ "python_executable_sha256": _sha256_text(
1538
+ data["python_executable_sha256"],
1539
+ label="ReFormeR Python executable SHA-256",
1540
+ ),
1541
+ "compatibility_manifest_path": _absolute_path(
1542
+ data["compatibility_manifest_path"],
1543
+ label="ReFormeR compatibility manifest",
1544
+ ),
1545
+ "compatibility_manifest_sha256": _sha256_text(
1546
+ data["compatibility_manifest_sha256"],
1547
+ label="ReFormeR compatibility manifest SHA-256",
1548
+ ),
1549
+ "base_url": _absolute_http_url(data["base_url"], label="ReFormeR base_url"),
1550
+ "api_key_env": _require_env_name(data["api_key_env"], label="ReFormeR api_key_env"),
1551
+ "model_id": _require_text(data["model_id"], label="ReFormeR model_id"),
1552
+ "extra_body": _canonical_mapping(
1553
+ _mapping_or_empty(data.get("extra_body"), path="ReFormeR extra_body"),
1554
+ label="ReFormeR extra_body",
1555
+ ),
1556
+ "timeout_seconds": _positive_number(
1557
+ data.get("timeout_seconds", 600.0), label="ReFormeR timeout_seconds"
1558
+ ),
1559
+ "max_input_tokens_per_call": _positive_int(
1560
+ data.get("max_input_tokens_per_call", 16384),
1561
+ label="ReFormeR max_input_tokens_per_call",
1562
+ ),
1563
+ "max_output_tokens": _positive_int(
1564
+ data.get("max_output_tokens", 512), label="ReFormeR max_output_tokens"
1565
+ ),
1566
+ "temperature": temperature,
1567
+ "input_cost_per_million": _nonnegative_number(
1568
+ data.get("input_cost_per_million", 0.0),
1569
+ label="ReFormeR input_cost_per_million",
1570
+ ),
1571
+ "output_cost_per_million": _nonnegative_number(
1572
+ data.get("output_cost_per_million", 0.0),
1573
+ label="ReFormeR output_cost_per_million",
1574
+ ),
1575
+ }
1576
+ )
1577
+ if kind == "qrag_common_support":
1578
+ required = {
1579
+ "kind",
1580
+ "source_root",
1581
+ "checkpoint_root",
1582
+ "embedder_root",
1583
+ "compatibility_manifest_path",
1584
+ "compatibility_manifest_sha256",
1585
+ "embedder_manifest_sha256",
1586
+ }
1587
+ _require_exact_backend_fields(
1588
+ data,
1589
+ required | {"device"},
1590
+ required=required,
1591
+ kind="Q-RAG external runtime",
1592
+ )
1593
+ return MappingProxyType(
1594
+ {
1595
+ "kind": kind,
1596
+ "source_root": _absolute_path(data["source_root"], label="Q-RAG source root"),
1597
+ "checkpoint_root": _absolute_path(
1598
+ data["checkpoint_root"], label="Q-RAG checkpoint root"
1599
+ ),
1600
+ "embedder_root": _absolute_path(data["embedder_root"], label="Q-RAG embedder root"),
1601
+ "compatibility_manifest_path": _absolute_path(
1602
+ data["compatibility_manifest_path"],
1603
+ label="Q-RAG compatibility manifest",
1604
+ ),
1605
+ "compatibility_manifest_sha256": _sha256_text(
1606
+ data["compatibility_manifest_sha256"],
1607
+ label="Q-RAG compatibility manifest SHA-256",
1608
+ ),
1609
+ "embedder_manifest_sha256": _sha256_text(
1610
+ data["embedder_manifest_sha256"],
1611
+ label="Q-RAG embedder manifest SHA-256",
1612
+ ),
1613
+ "device": _require_text(data.get("device", "cuda"), label="Q-RAG device"),
1614
+ }
1615
+ )
1616
+ if kind == "smartsearch_worker":
1617
+ required = {
1618
+ "kind",
1619
+ "checkout",
1620
+ "driver_path",
1621
+ "driver_sha256",
1622
+ "python_executable",
1623
+ "python_executable_sha256",
1624
+ "compatibility_manifest_path",
1625
+ "compatibility_manifest_sha256",
1626
+ "runtime_manifest_path",
1627
+ "runtime_manifest_sha256",
1628
+ }
1629
+ _require_exact_backend_fields(
1630
+ data,
1631
+ required | {"timeout_seconds"},
1632
+ required=required,
1633
+ kind="SmartSearch external runtime",
1634
+ )
1635
+ driver_path = _absolute_path(data["driver_path"], label="SmartSearch driver")
1636
+ compatibility_manifest_path = _absolute_path(
1637
+ data["compatibility_manifest_path"], label="SmartSearch compatibility manifest"
1638
+ )
1639
+ if compatibility_manifest_path != driver_path.with_name("compatibility_manifest.json"):
1640
+ raise ValidationError(
1641
+ "SmartSearch compatibility manifest must be the worker driver's sibling manifest"
1642
+ )
1643
+ return MappingProxyType(
1644
+ {
1645
+ "kind": kind,
1646
+ "checkout": _absolute_path(data["checkout"], label="SmartSearch checkout"),
1647
+ "driver_path": driver_path,
1648
+ "driver_sha256": _sha256_text(
1649
+ data["driver_sha256"], label="SmartSearch driver SHA-256"
1650
+ ),
1651
+ "python_executable": _absolute_path(
1652
+ data["python_executable"], label="SmartSearch Python executable"
1653
+ ),
1654
+ "python_executable_sha256": _sha256_text(
1655
+ data["python_executable_sha256"],
1656
+ label="SmartSearch Python executable SHA-256",
1657
+ ),
1658
+ "compatibility_manifest_path": compatibility_manifest_path,
1659
+ "compatibility_manifest_sha256": _sha256_text(
1660
+ data["compatibility_manifest_sha256"],
1661
+ label="SmartSearch compatibility manifest SHA-256",
1662
+ ),
1663
+ "runtime_manifest_path": _absolute_path(
1664
+ data["runtime_manifest_path"], label="SmartSearch runtime manifest"
1665
+ ),
1666
+ "runtime_manifest_sha256": _sha256_text(
1667
+ data["runtime_manifest_sha256"],
1668
+ label="SmartSearch runtime manifest SHA-256",
1669
+ ),
1670
+ "timeout_seconds": _positive_number(
1671
+ data.get("timeout_seconds", 1800.0),
1672
+ label="SmartSearch timeout_seconds",
1673
+ ),
1674
+ }
1675
+ )
1676
+ if kind == "r2ag_common_support":
1677
+ required = {
1678
+ "kind",
1679
+ "artifact_root",
1680
+ "manifest_sha256",
1681
+ "source_root",
1682
+ "base_model_root",
1683
+ "base_model_load_set_sha256",
1684
+ "feature_model_root",
1685
+ "feature_model_load_set_sha256",
1686
+ }
1687
+ _require_exact_backend_fields(
1688
+ data,
1689
+ required | {"reader_device", "feature_device"},
1690
+ required=required,
1691
+ kind="R²AG external runtime",
1692
+ )
1693
+ return MappingProxyType(
1694
+ {
1695
+ "kind": kind,
1696
+ "artifact_root": _absolute_path(data["artifact_root"], label="R²AG artifact root"),
1697
+ "manifest_sha256": _sha256_text(
1698
+ data["manifest_sha256"], label="R²AG manifest_sha256"
1699
+ ),
1700
+ "source_root": _absolute_path(data["source_root"], label="R²AG source root"),
1701
+ "base_model_root": _absolute_path(
1702
+ data["base_model_root"], label="R²AG base model root"
1703
+ ),
1704
+ "base_model_load_set_sha256": _sha256_text(
1705
+ data["base_model_load_set_sha256"],
1706
+ label="R²AG base model load-set SHA-256",
1707
+ ),
1708
+ "feature_model_root": _absolute_path(
1709
+ data["feature_model_root"], label="R²AG feature model root"
1710
+ ),
1711
+ "feature_model_load_set_sha256": _sha256_text(
1712
+ data["feature_model_load_set_sha256"],
1713
+ label="R²AG feature model load-set SHA-256",
1714
+ ),
1715
+ "reader_device": _require_text(
1716
+ data.get("reader_device", "cuda"), label="R²AG reader device"
1717
+ ),
1718
+ "feature_device": _require_text(
1719
+ data.get("feature_device", "cuda"), label="R²AG feature device"
1720
+ ),
1721
+ }
1722
+ )
1723
+ raise ValidationError(f"unsupported production external runtime kind: {kind!r}")
1724
+
1725
+
1726
+ def _parse_backend_block(value: Any) -> Mapping[str, Any]:
1727
+ base = _strict_mapping(
1728
+ value,
1729
+ path="backend parameters.backend",
1730
+ allowed={
1731
+ "kind",
1732
+ "model_id",
1733
+ "capabilities",
1734
+ "fixture_path",
1735
+ "fixture_sha256",
1736
+ "base_url",
1737
+ "deployment_identity",
1738
+ "deployment_revision_env",
1739
+ "api_key_env",
1740
+ "require_api_key",
1741
+ "timeout_seconds",
1742
+ "extra_headers",
1743
+ "extra_body",
1744
+ "model_revision",
1745
+ "tokenizer_revision",
1746
+ "model_path",
1747
+ "generation_kwargs",
1748
+ "local_files_only",
1749
+ "artifact_files",
1750
+ },
1751
+ required={"kind", "model_id"},
1752
+ )
1753
+ kind = _require_text(base["kind"], label="backend kind")
1754
+ if kind not in {"replay", "openai_compatible", "transformers"}:
1755
+ raise ValidationError(f"unsupported production backend kind: {kind!r}")
1756
+ model_id = _require_text(base["model_id"], label="backend model_id")
1757
+ defaults = {
1758
+ "replay": (
1759
+ ModelCapability.CHAT,
1760
+ ModelCapability.COMPLETION,
1761
+ ModelCapability.SEED,
1762
+ ),
1763
+ "openai_compatible": (
1764
+ ModelCapability.CHAT,
1765
+ ModelCapability.COMPLETION,
1766
+ ModelCapability.JSON_OBJECT,
1767
+ ModelCapability.TOOL_CALLING,
1768
+ ModelCapability.SEED,
1769
+ ),
1770
+ "transformers": (
1771
+ ModelCapability.CHAT,
1772
+ ModelCapability.COMPLETION,
1773
+ ),
1774
+ }
1775
+ capabilities = _capabilities(
1776
+ base.get("capabilities", tuple(item.value for item in defaults[kind])),
1777
+ path="backend capabilities",
1778
+ )
1779
+ common = {
1780
+ "kind": kind,
1781
+ "model_id": model_id,
1782
+ "capabilities": capabilities,
1783
+ }
1784
+ if kind == "replay":
1785
+ _require_exact_backend_fields(
1786
+ base,
1787
+ {"kind", "model_id", "capabilities", "fixture_path", "fixture_sha256"},
1788
+ required={"fixture_path", "fixture_sha256"},
1789
+ kind=kind,
1790
+ )
1791
+ return MappingProxyType(
1792
+ {
1793
+ **common,
1794
+ "fixture_path": _absolute_path(
1795
+ base["fixture_path"],
1796
+ label="replay fixture_path",
1797
+ ),
1798
+ "fixture_sha256": _sha256_text(
1799
+ base["fixture_sha256"],
1800
+ label="replay fixture_sha256",
1801
+ ),
1802
+ }
1803
+ )
1804
+ if kind == "openai_compatible":
1805
+ _require_exact_backend_fields(
1806
+ base,
1807
+ {
1808
+ "kind",
1809
+ "model_id",
1810
+ "capabilities",
1811
+ "base_url",
1812
+ "deployment_identity",
1813
+ "deployment_revision_env",
1814
+ "api_key_env",
1815
+ "require_api_key",
1816
+ "timeout_seconds",
1817
+ "extra_headers",
1818
+ "extra_body",
1819
+ },
1820
+ required={"base_url", "deployment_identity"},
1821
+ kind=kind,
1822
+ )
1823
+ api_key_env = base.get("api_key_env", "OPENAI_API_KEY")
1824
+ _require_env_name(api_key_env, label="OpenAI-compatible api_key_env")
1825
+ require_api_key = base.get("require_api_key", False)
1826
+ if not isinstance(require_api_key, bool):
1827
+ raise ValidationError("OpenAI-compatible require_api_key must be boolean")
1828
+ timeout = _positive_number(
1829
+ base.get("timeout_seconds", 120.0),
1830
+ label="OpenAI-compatible timeout_seconds",
1831
+ )
1832
+ headers = _mapping_or_empty(
1833
+ base.get("extra_headers"),
1834
+ path="OpenAI-compatible extra_headers",
1835
+ )
1836
+ for name, header_value in headers.items():
1837
+ _require_text(name, label="OpenAI-compatible header name")
1838
+ if _SENSITIVE_HEADER.fullmatch(name):
1839
+ raise ValidationError(f"sensitive header {name!r} is forbidden; use api_key_env")
1840
+ _require_text(header_value, label=f"OpenAI-compatible header {name}")
1841
+ identity = _canonical_mapping(
1842
+ _mapping_or_empty(
1843
+ base["deployment_identity"],
1844
+ path="OpenAI-compatible deployment_identity",
1845
+ ),
1846
+ label="OpenAI-compatible deployment_identity",
1847
+ )
1848
+ if not identity.get("revision"):
1849
+ raise ValidationError(
1850
+ "OpenAI-compatible deployment_identity requires a non-empty revision"
1851
+ )
1852
+ deployment_revision_env = base.get("deployment_revision_env")
1853
+ if deployment_revision_env is not None:
1854
+ _require_env_name(
1855
+ deployment_revision_env,
1856
+ label="OpenAI-compatible deployment_revision_env",
1857
+ )
1858
+ return MappingProxyType(
1859
+ {
1860
+ **common,
1861
+ "base_url": _absolute_http_url(
1862
+ base["base_url"],
1863
+ label="OpenAI-compatible base_url",
1864
+ ),
1865
+ "deployment_identity": identity,
1866
+ "deployment_revision_env": deployment_revision_env,
1867
+ "api_key_env": api_key_env,
1868
+ "require_api_key": require_api_key,
1869
+ "timeout_seconds": timeout,
1870
+ "extra_headers": _canonical_mapping(headers, label="extra_headers"),
1871
+ "extra_body": _canonical_mapping(
1872
+ _mapping_or_empty(
1873
+ base.get("extra_body"),
1874
+ path="OpenAI-compatible extra_body",
1875
+ ),
1876
+ label="extra_body",
1877
+ ),
1878
+ }
1879
+ )
1880
+ _require_exact_backend_fields(
1881
+ base,
1882
+ {
1883
+ "kind",
1884
+ "model_id",
1885
+ "capabilities",
1886
+ "model_revision",
1887
+ "tokenizer_revision",
1888
+ "model_path",
1889
+ "generation_kwargs",
1890
+ "local_files_only",
1891
+ "artifact_files",
1892
+ },
1893
+ required={
1894
+ "model_path",
1895
+ "model_revision",
1896
+ "tokenizer_revision",
1897
+ "artifact_files",
1898
+ },
1899
+ kind=kind,
1900
+ )
1901
+ local_files_only = base.get("local_files_only", True)
1902
+ if local_files_only is not True:
1903
+ raise ValidationError("production Transformers backend requires local_files_only=true")
1904
+ artifacts = _artifact_file_locks(
1905
+ base["artifact_files"],
1906
+ path="Transformers artifact_files",
1907
+ )
1908
+ if not artifacts:
1909
+ raise ValidationError(
1910
+ "production Transformers backend requires checksum-locked artifact_files"
1911
+ )
1912
+ model_path = _absolute_path(
1913
+ base["model_path"],
1914
+ label="Transformers model_path",
1915
+ )
1916
+ _validate_transformers_artifact_layout(model_path, artifacts)
1917
+ return MappingProxyType(
1918
+ {
1919
+ **common,
1920
+ "model_path": model_path,
1921
+ "model_revision": _require_text(
1922
+ base["model_revision"],
1923
+ label="Transformers model_revision",
1924
+ ),
1925
+ "tokenizer_revision": _require_text(
1926
+ base["tokenizer_revision"],
1927
+ label="Transformers tokenizer_revision",
1928
+ ),
1929
+ "generation_kwargs": _canonical_mapping(
1930
+ _mapping_or_empty(
1931
+ base.get("generation_kwargs"),
1932
+ path="Transformers generation_kwargs",
1933
+ ),
1934
+ label="Transformers generation_kwargs",
1935
+ ),
1936
+ "local_files_only": True,
1937
+ "artifact_files": artifacts,
1938
+ }
1939
+ )
1940
+
1941
+
1942
+ def _require_exact_backend_fields(
1943
+ data: Mapping[str, Any],
1944
+ allowed: set[str],
1945
+ *,
1946
+ required: set[str],
1947
+ kind: str,
1948
+ ) -> None:
1949
+ unknown = set(data) - allowed
1950
+ missing = required - set(data)
1951
+ if unknown:
1952
+ raise ValidationError(
1953
+ f"{kind} backend has incompatible fields: {', '.join(sorted(unknown))}"
1954
+ )
1955
+ if missing:
1956
+ raise ValidationError(f"{kind} backend is missing fields: {', '.join(sorted(missing))}")
1957
+
1958
+
1959
+ def _backend_preflight_blockers(config: Mapping[str, Any]) -> list[str]:
1960
+ blockers = [
1961
+ f"required environment variable {name!r} is not set"
1962
+ for name in config["required_env"]
1963
+ if not os.environ.get(name)
1964
+ ]
1965
+ backend_config = config["backend"]
1966
+ if (
1967
+ backend_config["kind"] == "openai_compatible"
1968
+ and backend_config["deployment_revision_env"] is not None
1969
+ ):
1970
+ revision_environment = backend_config["deployment_revision_env"]
1971
+ observed_revision = os.environ.get(revision_environment)
1972
+ if (
1973
+ observed_revision
1974
+ and observed_revision != backend_config["deployment_identity"]["revision"]
1975
+ ):
1976
+ blockers.append(
1977
+ "OpenAI-compatible deployment revision differs from its environment lock"
1978
+ )
1979
+ try:
1980
+ backend = _build_llm_backend(backend_config)
1981
+ require_capabilities(backend, config["required_capabilities"])
1982
+ _build_external_runtime(config["external_runtime"], backend)
1983
+ except (ArtifactError, CapabilityError, ExternalDependencyError, ValidationError) as exc:
1984
+ blockers.append(str(exc))
1985
+ if backend_config["kind"] == "transformers":
1986
+ if importlib.util.find_spec("transformers") is None:
1987
+ blockers.append("transformers is unavailable; install agentinputlab[local]")
1988
+ try:
1989
+ _verify_transformers_snapshot(
1990
+ backend_config["model_path"],
1991
+ backend_config["artifact_files"],
1992
+ )
1993
+ except ArtifactError as exc:
1994
+ blockers.append(str(exc))
1995
+ for benchmark_id, spec in config["retrievers"].items():
1996
+ try:
1997
+ builder = _resolve_retriever_builder(spec)
1998
+ messages = builder.preflight(spec.parameters)
1999
+ if isinstance(messages, (str, bytes)) or not isinstance(messages, Sequence):
2000
+ raise ValidationError("retriever builder preflight must return a sequence")
2001
+ for message in messages:
2002
+ if not isinstance(message, str) or not message.strip():
2003
+ raise ValidationError(
2004
+ "retriever builder preflight blockers must be non-empty strings"
2005
+ )
2006
+ blockers.append(f"retriever {benchmark_id}: {message}")
2007
+ except (ArtifactError, TypeError, ValueError) as exc:
2008
+ blockers.append(f"retriever {benchmark_id}: {exc}")
2009
+ vector_spec = config["vector_runtime"]
2010
+ if vector_spec is not None:
2011
+ try:
2012
+ builder = _resolve_retriever_builder(vector_spec)
2013
+ messages = builder.preflight(vector_spec.parameters)
2014
+ if isinstance(messages, (str, bytes)) or not isinstance(messages, Sequence):
2015
+ raise ValidationError("vector runtime preflight must return a sequence")
2016
+ for message in messages:
2017
+ if not isinstance(message, str) or not message.strip():
2018
+ raise ValidationError(
2019
+ "vector runtime preflight blockers must be non-empty strings"
2020
+ )
2021
+ blockers.append(f"vector runtime: {message}")
2022
+ except (ArtifactError, TypeError, ValueError) as exc:
2023
+ blockers.append(f"vector runtime: {exc}")
2024
+ return blockers
2025
+
2026
+
2027
+ def _build_llm_backend(config: Mapping[str, Any]) -> LLMBackend:
2028
+ kind = config["kind"]
2029
+ if kind == "replay":
2030
+ fixture = _load_replay_fixture(
2031
+ config["fixture_path"],
2032
+ config["fixture_sha256"],
2033
+ )
2034
+ inner: LLMBackend = ReplayBackend(
2035
+ fixture["responses"],
2036
+ model_id=config["model_id"],
2037
+ capabilities=frozenset(config["capabilities"]),
2038
+ strict_request_hashes=fixture["strict_request_hashes"],
2039
+ )
2040
+ binding = {
2041
+ "kind": kind,
2042
+ "fixture_sha256": config["fixture_sha256"],
2043
+ "fixture_schema_version": _SCHEMA_VERSION,
2044
+ }
2045
+ elif kind == "openai_compatible":
2046
+ inner = OpenAICompatibleBackend(
2047
+ model_id=config["model_id"],
2048
+ base_url=config["base_url"],
2049
+ deployment_identity=config["deployment_identity"],
2050
+ api_key=None,
2051
+ api_key_env=config["api_key_env"],
2052
+ timeout_seconds=config["timeout_seconds"],
2053
+ extra_headers=config["extra_headers"],
2054
+ extra_body=config["extra_body"],
2055
+ capabilities=frozenset(config["capabilities"]),
2056
+ )
2057
+ binding = {
2058
+ "kind": kind,
2059
+ "deployment_revision_source": (
2060
+ {
2061
+ "kind": "environment",
2062
+ "name": config["deployment_revision_env"],
2063
+ }
2064
+ if config["deployment_revision_env"] is not None
2065
+ else {"kind": "operator_declaration"}
2066
+ ),
2067
+ "api_key_source": {
2068
+ "kind": "environment",
2069
+ "name": config["api_key_env"],
2070
+ "required": config["require_api_key"],
2071
+ },
2072
+ }
2073
+ else:
2074
+ inner = _LocalOnlyTransformersBackend(
2075
+ model_id=config["model_id"],
2076
+ model_path=config["model_path"],
2077
+ model_revision=config["model_revision"],
2078
+ tokenizer_revision=config["tokenizer_revision"],
2079
+ artifact_files=config["artifact_files"],
2080
+ generation_kwargs=config["generation_kwargs"],
2081
+ capabilities=frozenset(config["capabilities"]),
2082
+ local_files_only=config["local_files_only"],
2083
+ )
2084
+ binding = {
2085
+ "kind": kind,
2086
+ "snapshot_sha256": _transformers_snapshot_sha256(config["artifact_files"]),
2087
+ "artifact_files": tuple(
2088
+ {"name": artifact["name"], "sha256": artifact["sha256"]}
2089
+ for artifact in config["artifact_files"]
2090
+ ),
2091
+ "local_files_only": True,
2092
+ }
2093
+ return _BoundLLMBackend(inner, binding)
2094
+
2095
+
2096
+ @lru_cache(maxsize=4)
2097
+ def _cached_recomp_extractive_model(
2098
+ checkpoint_root: str,
2099
+ device: str,
2100
+ ) -> tuple[Any, Any]:
2101
+ """Load the frozen RECOMP artifact with the installed Transformers API.
2102
+
2103
+ RECOMP's scorer is invoked unchanged by its worker. Only the historical
2104
+ ``from_pretrained`` compatibility surface is adapted; this identity and
2105
+ the installed package versions remain visible in the worker fingerprint.
2106
+ """
2107
+
2108
+ try:
2109
+ from transformers import AutoModel, AutoTokenizer
2110
+ except (ImportError, ModuleNotFoundError) as exc:
2111
+ raise ExternalDependencyError(
2112
+ "RECOMP current-runtime loader requires transformers"
2113
+ ) from exc
2114
+ tokenizer = AutoTokenizer.from_pretrained(
2115
+ checkpoint_root,
2116
+ local_files_only=True,
2117
+ trust_remote_code=False,
2118
+ )
2119
+ model = AutoModel.from_pretrained(
2120
+ checkpoint_root,
2121
+ local_files_only=True,
2122
+ trust_remote_code=False,
2123
+ )
2124
+ model = model.to(device)
2125
+ model.eval()
2126
+ return model, tokenizer
2127
+
2128
+
2129
+ def _load_recomp_extractive_current_runtime(
2130
+ mode: str,
2131
+ checkpoint_root: Path,
2132
+ device: str,
2133
+ ) -> tuple[Any, Any]:
2134
+ if mode != "extractive":
2135
+ raise ValidationError("RECOMP production runtime only accepts the extractive release")
2136
+ return _cached_recomp_extractive_model(str(checkpoint_root), device)
2137
+
2138
+
2139
+ def _build_external_runtime(
2140
+ config: Mapping[str, Any] | None,
2141
+ backend: LLMBackend,
2142
+ ) -> Any:
2143
+ if config is None:
2144
+ return None
2145
+ if config["kind"] == "querygym_query2doc":
2146
+ from agentinputlab.algorithms.reformulate.query2doc import PinnedQueryGymRuntime
2147
+
2148
+ runtime = PinnedQueryGymRuntime(config["checkout"], backend)
2149
+ identity = _fingerprint(runtime, label="QueryGym external runtime")
2150
+ if identity.get("implementation") != _QUERYGYM_RUNTIME_IMPLEMENTATION:
2151
+ raise ValidationError("QueryGym runtime implementation identity is invalid")
2152
+ return runtime
2153
+ if config["kind"] == "recomp_extractive_worker":
2154
+ from workers.recomp.driver import RECOMPDriver
2155
+
2156
+ _verify_locked_file(
2157
+ config["checkpoint_manifest_path"],
2158
+ config["checkpoint_manifest_sha256"],
2159
+ label="RECOMP checkpoint manifest",
2160
+ )
2161
+ _verify_locked_file(
2162
+ config["compatibility_manifest_path"],
2163
+ config["compatibility_manifest_sha256"],
2164
+ label="RECOMP compatibility manifest",
2165
+ )
2166
+ return RECOMPDriver(
2167
+ checkout_path=config["checkout"],
2168
+ checkpoint_path=config["checkpoint_root"],
2169
+ checkpoint_manifest_path=config["checkpoint_manifest_path"],
2170
+ device=config["device"],
2171
+ manifest_path=config["compatibility_manifest_path"],
2172
+ model_loader=_load_recomp_extractive_current_runtime,
2173
+ checkpoint_license_acknowledged=True,
2174
+ )
2175
+ if config["kind"] == "acon_observation_worker":
2176
+ from agentinputlab.integrations.external_workers import JsonLineSubprocessWorker
2177
+
2178
+ driver_sha256 = _verify_locked_file(
2179
+ config["driver_path"], config["driver_sha256"], label="ACON driver"
2180
+ )
2181
+ executable_sha256 = _verify_locked_file(
2182
+ config["python_executable"],
2183
+ config["python_executable_sha256"],
2184
+ label="ACON Python executable",
2185
+ )
2186
+ manifest_sha256 = _verify_locked_file(
2187
+ config["compatibility_manifest_path"],
2188
+ config["compatibility_manifest_sha256"],
2189
+ label="ACON compatibility manifest",
2190
+ )
2191
+ model_fingerprint = dict(_fingerprint(backend, label="ACON target backend"))
2192
+ environment = {
2193
+ "AGENTINPUTLAB_ACON_CHECKOUT": str(config["checkout"]),
2194
+ "AGENTINPUTLAB_ACON_OPENAI_BASE_URL": config["base_url"],
2195
+ "AGENTINPUTLAB_ACON_OPTIMIZER_MODEL": config["model_id"],
2196
+ "AGENTINPUTLAB_ACON_OPTIMIZER_MODEL_REVISION": (config["optimizer_model_revision"]),
2197
+ "AGENTINPUTLAB_ACON_TARGET_MODEL_REVISION": config["target_model_revision"],
2198
+ "AGENTINPUTLAB_ACON_TOKENIZER_REVISION": config["tokenizer_revision"],
2199
+ "AGENTINPUTLAB_ACON_TEMPERATURE": str(config["temperature"]),
2200
+ "AGENTINPUTLAB_ACON_TIMEOUT_SECONDS": str(config["timeout_seconds"]),
2201
+ "AGENTINPUTLAB_ACON_MAX_TOKENS": str(config["max_output_tokens"]),
2202
+ "AGENTINPUTLAB_ACON_EXTRA_BODY_JSON": json.dumps(
2203
+ to_jsonable(config["extra_body"]),
2204
+ ensure_ascii=False,
2205
+ sort_keys=True,
2206
+ separators=(",", ":"),
2207
+ allow_nan=False,
2208
+ ),
2209
+ "PYTHONDONTWRITEBYTECODE": "1",
2210
+ }
2211
+ pass_environment = (config["api_key_env"],) if config["api_key_env"] is not None else ()
2212
+ return JsonLineSubprocessWorker(
2213
+ command=(str(config["python_executable"]), str(config["driver_path"])),
2214
+ worker_id="acon-common-support-observation",
2215
+ identity={
2216
+ "implementation": "agentinputlab.acon.process_worker.v1",
2217
+ "release_id": "curate.acon.common_support_observation",
2218
+ "component_release_id": "curate.acon.observation_runtime",
2219
+ "upstream_commit": "d63f9ae18959dc7215ff62899c94c5e8c56847ae",
2220
+ "driver_sha256": driver_sha256,
2221
+ "python_executable_sha256": executable_sha256,
2222
+ "compatibility_manifest_sha256": manifest_sha256,
2223
+ "optimizer_endpoint": config["base_url"],
2224
+ "optimizer_model_revision": config["optimizer_model_revision"],
2225
+ "target_model_revision": config["target_model_revision"],
2226
+ "tokenizer_revision": config["tokenizer_revision"],
2227
+ "optimizer_extra_body_hash": content_hash(config["extra_body"]),
2228
+ "target_backend": model_fingerprint,
2229
+ },
2230
+ timeout_seconds=config["timeout_seconds"],
2231
+ pass_environment=pass_environment,
2232
+ working_directory=str(config["driver_path"].parents[2]),
2233
+ environment=environment,
2234
+ )
2235
+ if config["kind"] == "ask_or_assume_worker":
2236
+ from agentinputlab.integrations.external_workers import JsonLineSubprocessWorker
2237
+
2238
+ driver_sha256 = _verify_locked_file(
2239
+ config["driver_path"],
2240
+ config["driver_sha256"],
2241
+ label="Ask-or-Assume driver",
2242
+ )
2243
+ executable_sha256 = _verify_locked_file(
2244
+ config["python_executable"],
2245
+ config["python_executable_sha256"],
2246
+ label="Ask-or-Assume Python executable",
2247
+ )
2248
+ manifest_sha256 = _verify_locked_file(
2249
+ config["compatibility_manifest_path"],
2250
+ config["compatibility_manifest_sha256"],
2251
+ label="Ask-or-Assume compatibility manifest",
2252
+ )
2253
+ model_fingerprint = dict(_fingerprint(backend, label="Ask-or-Assume backend"))
2254
+ role = {
2255
+ "model": config["model_id"],
2256
+ "fingerprint": model_fingerprint,
2257
+ "max_output_tokens": config["max_output_tokens"],
2258
+ "input_cost_per_million": config["input_cost_per_million"],
2259
+ "output_cost_per_million": config["output_cost_per_million"],
2260
+ }
2261
+ backend_config = {
2262
+ "base_url": config["base_url"],
2263
+ "api_key_env": config["api_key_env"],
2264
+ "timeout_seconds": config["timeout_seconds"],
2265
+ "roles": {"intent": role, "coding": role},
2266
+ }
2267
+ return JsonLineSubprocessWorker(
2268
+ command=(
2269
+ str(config["python_executable"]),
2270
+ str(config["driver_path"]),
2271
+ "--checkout",
2272
+ str(config["checkout"]),
2273
+ "--backend-config-json",
2274
+ json.dumps(
2275
+ to_jsonable(backend_config),
2276
+ ensure_ascii=False,
2277
+ sort_keys=True,
2278
+ separators=(",", ":"),
2279
+ allow_nan=False,
2280
+ ),
2281
+ ),
2282
+ worker_id="ask-or-assume-intent-monitor",
2283
+ identity={
2284
+ "implementation": "agentinputlab.ask_or_assume.process_worker.v1",
2285
+ "release_id": "clarify.ask_or_assume.intent_monitor_component",
2286
+ "upstream_commit": "728464a755f15fd448aff61a5cb32dce97fd9c1d",
2287
+ "driver_sha256": driver_sha256,
2288
+ "python_executable_sha256": executable_sha256,
2289
+ "compatibility_manifest_sha256": manifest_sha256,
2290
+ "backend": model_fingerprint,
2291
+ },
2292
+ timeout_seconds=config["timeout_seconds"],
2293
+ pass_environment=(config["api_key_env"],),
2294
+ working_directory=str(config["driver_path"].parents[2]),
2295
+ )
2296
+ if config["kind"] in {"reformer_worker", "infocqr_worker"}:
2297
+ from agentinputlab.integrations.external_workers import JsonLineSubprocessWorker
2298
+
2299
+ is_infocqr = config["kind"] == "infocqr_worker"
2300
+ runtime_label = "InfoCQR" if is_infocqr else "ReFormeR"
2301
+ driver_sha256 = _verify_locked_file(
2302
+ config["driver_path"], config["driver_sha256"], label=f"{runtime_label} driver"
2303
+ )
2304
+ executable_sha256 = _verify_locked_file(
2305
+ config["python_executable"],
2306
+ config["python_executable_sha256"],
2307
+ label="ReFormeR Python executable",
2308
+ )
2309
+ manifest_sha256 = _verify_locked_file(
2310
+ config["compatibility_manifest_path"],
2311
+ config["compatibility_manifest_sha256"],
2312
+ label=f"{runtime_label} compatibility manifest",
2313
+ )
2314
+ model_fingerprint = dict(_fingerprint(backend, label="ReFormeR backend"))
2315
+ backend_config = {
2316
+ "base_url": config["base_url"],
2317
+ "api_key_env": config["api_key_env"],
2318
+ "model": config["model_id"],
2319
+ "fingerprint": model_fingerprint,
2320
+ "extra_body": config["extra_body"],
2321
+ "timeout_seconds": config["timeout_seconds"],
2322
+ "max_input_tokens_per_call": config["max_input_tokens_per_call"],
2323
+ "max_output_tokens": config["max_output_tokens"],
2324
+ "temperature": config["temperature"],
2325
+ "input_cost_per_million": config["input_cost_per_million"],
2326
+ "output_cost_per_million": config["output_cost_per_million"],
2327
+ }
2328
+ return JsonLineSubprocessWorker(
2329
+ command=(
2330
+ str(config["python_executable"]),
2331
+ str(config["driver_path"]),
2332
+ "--checkout",
2333
+ str(config["checkout"]),
2334
+ "--manifest" if is_infocqr else "--compatibility-manifest",
2335
+ str(config["compatibility_manifest_path"]),
2336
+ "--backend-config-json",
2337
+ json.dumps(
2338
+ to_jsonable(backend_config),
2339
+ ensure_ascii=False,
2340
+ sort_keys=True,
2341
+ separators=(",", ":"),
2342
+ allow_nan=False,
2343
+ ),
2344
+ "--acknowledge-noassertion-license",
2345
+ ),
2346
+ worker_id=(
2347
+ "infocqr-common-support-rewriter" if is_infocqr else "reformer-pattern-reformulator"
2348
+ ),
2349
+ identity={
2350
+ "implementation": (
2351
+ "agentinputlab.infocqr.process_worker.v1"
2352
+ if is_infocqr
2353
+ else "agentinputlab.reformer.process_worker.v1"
2354
+ ),
2355
+ "release_id": (
2356
+ "rewrite.infocqr.common_support_rw_zsl"
2357
+ if is_infocqr
2358
+ else "rewrite.reformer.repo_native_r_only"
2359
+ ),
2360
+ "upstream_commit": (
2361
+ "2953c461aa0c6817b1b7656dbd5168050227b31d"
2362
+ if is_infocqr
2363
+ else "72e52450a922bc0051b5b39c3a6307186a555137"
2364
+ ),
2365
+ "driver_sha256": driver_sha256,
2366
+ "python_executable_sha256": executable_sha256,
2367
+ "compatibility_manifest_sha256": manifest_sha256,
2368
+ "backend": model_fingerprint,
2369
+ },
2370
+ timeout_seconds=config["timeout_seconds"],
2371
+ pass_environment=(config["api_key_env"],),
2372
+ working_directory=str(config["driver_path"].parents[2]),
2373
+ environment={"PYTHONDONTWRITEBYTECODE": "1"},
2374
+ )
2375
+ if config["kind"] == "qrag_common_support":
2376
+ from agentinputlab.algorithms.acquire.qrag import LocalQRAGFrozenPolicyRuntime
2377
+
2378
+ return LocalQRAGFrozenPolicyRuntime(
2379
+ source_root=config["source_root"],
2380
+ checkpoint_root=config["checkpoint_root"],
2381
+ embedder_root=config["embedder_root"],
2382
+ compatibility_manifest_path=config["compatibility_manifest_path"],
2383
+ compatibility_manifest_sha256=config["compatibility_manifest_sha256"],
2384
+ embedder_manifest_sha256=config["embedder_manifest_sha256"],
2385
+ device=config["device"],
2386
+ )
2387
+ if config["kind"] == "smartsearch_worker":
2388
+ from agentinputlab.integrations.external_workers import JsonLineSubprocessWorker
2389
+
2390
+ driver_sha256 = _verify_locked_file(
2391
+ config["driver_path"], config["driver_sha256"], label="SmartSearch driver"
2392
+ )
2393
+ executable_sha256 = _verify_locked_file(
2394
+ config["python_executable"],
2395
+ config["python_executable_sha256"],
2396
+ label="SmartSearch Python executable",
2397
+ )
2398
+ manifest_sha256 = _verify_locked_file(
2399
+ config["compatibility_manifest_path"],
2400
+ config["compatibility_manifest_sha256"],
2401
+ label="SmartSearch compatibility manifest",
2402
+ )
2403
+ runtime_manifest_sha256 = _verify_locked_file(
2404
+ config["runtime_manifest_path"],
2405
+ config["runtime_manifest_sha256"],
2406
+ label="SmartSearch runtime manifest",
2407
+ )
2408
+ model_fingerprint = dict(_fingerprint(backend, label="SmartSearch target backend"))
2409
+ return JsonLineSubprocessWorker(
2410
+ command=(str(config["python_executable"]), str(config["driver_path"])),
2411
+ worker_id="smartsearch-common-support-queries",
2412
+ identity={
2413
+ "implementation": "agentinputlab.smartsearch.process_worker.v2",
2414
+ "release_id": "rewrite.smartsearch.common_support_queries",
2415
+ "component_release_id": "recipe.smartsearch.frozen_3b_inference",
2416
+ "upstream_commit": "5e9e7ec0d6af34f45fad145d4bff442529e7e6ff",
2417
+ "driver_sha256": driver_sha256,
2418
+ "python_executable_sha256": executable_sha256,
2419
+ "compatibility_manifest_sha256": manifest_sha256,
2420
+ "runtime_manifest_sha256": runtime_manifest_sha256,
2421
+ "target_backend": model_fingerprint,
2422
+ },
2423
+ timeout_seconds=config["timeout_seconds"],
2424
+ working_directory=str(config["driver_path"].parents[2]),
2425
+ environment={
2426
+ "AGENTINPUTLAB_SMARTSEARCH_CHECKOUT": str(config["checkout"]),
2427
+ "AGENTINPUTLAB_SMARTSEARCH_RUNTIME_MANIFEST": str(config["runtime_manifest_path"]),
2428
+ "PYTHONDONTWRITEBYTECODE": "1",
2429
+ },
2430
+ )
2431
+ if config["kind"] == "r2ag_common_support":
2432
+ import os
2433
+
2434
+ from agentinputlab.algorithms.acquire.r2ag import (
2435
+ R2AG_RUNTIME_SOCKET_ENV,
2436
+ R2AG_RUNTIME_TIMEOUT_ENV,
2437
+ LocalR2AGCommonSupportReader,
2438
+ LocalR2AGCommonSupportRuntime,
2439
+ LocalSentenceTransformerR2AGFeatureEncoder,
2440
+ UnixSocketR2AGCommonSupportRuntime,
2441
+ )
2442
+
2443
+ socket_path = os.environ.get(R2AG_RUNTIME_SOCKET_ENV)
2444
+ if socket_path:
2445
+ return UnixSocketR2AGCommonSupportRuntime(
2446
+ socket_path=Path(socket_path),
2447
+ timeout_seconds=float(os.environ.get(R2AG_RUNTIME_TIMEOUT_ENV, "3600")),
2448
+ )
2449
+
2450
+ return LocalR2AGCommonSupportRuntime(
2451
+ reader=LocalR2AGCommonSupportReader(
2452
+ artifact_root=config["artifact_root"],
2453
+ manifest_sha256=config["manifest_sha256"],
2454
+ source_root=config["source_root"],
2455
+ base_model_root=config["base_model_root"],
2456
+ base_model_load_set_sha256=config["base_model_load_set_sha256"],
2457
+ device=config["reader_device"],
2458
+ ),
2459
+ feature_encoder=LocalSentenceTransformerR2AGFeatureEncoder(
2460
+ model_root=config["feature_model_root"],
2461
+ model_load_set_sha256=config["feature_model_load_set_sha256"],
2462
+ device=config["feature_device"],
2463
+ ),
2464
+ )
2465
+ raise ValidationError(f"unsupported external runtime kind: {config['kind']!r}")
2466
+
2467
+
2468
+ def _parse_predictor_parameters(parameters: Mapping[str, Any]) -> Mapping[str, Any]:
2469
+ _reject_embedded_secrets(parameters, path="predictor parameters")
2470
+ data = _strict_mapping(
2471
+ parameters,
2472
+ path="predictor parameters",
2473
+ allowed={"schema_version", "task", "qa", "retrieval"},
2474
+ required={"schema_version"},
2475
+ )
2476
+ _require_schema_version(data["schema_version"], path="predictor parameters.schema_version")
2477
+ task = data.get("task", "auto")
2478
+ if task not in {"auto", "qa", "retrieval"}:
2479
+ raise ValidationError("predictor task must be auto, qa, or retrieval")
2480
+ qa = _strict_mapping(
2481
+ data.get("qa", {}),
2482
+ path="predictor parameters.qa",
2483
+ allowed={
2484
+ "system_prompt",
2485
+ "prompt_revision",
2486
+ "output_format",
2487
+ "structured_output_mode",
2488
+ "identifier_policy",
2489
+ "max_tokens",
2490
+ "temperature",
2491
+ "stop",
2492
+ "use_seed",
2493
+ "required_capabilities",
2494
+ },
2495
+ required=set(),
2496
+ )
2497
+ output_format = qa.get("output_format", "structured_json")
2498
+ if output_format not in {"structured_json", "answer_only"}:
2499
+ raise ValidationError("QA output_format must be structured_json or answer_only")
2500
+ structured_output_mode = qa.get("structured_output_mode", "json_object")
2501
+ if structured_output_mode not in {"json_object", "json_schema"}:
2502
+ raise ValidationError("QA structured_output_mode must be json_object or json_schema")
2503
+ identifier_policy = qa.get("identifier_policy", "native_sets.v1")
2504
+ if not isinstance(identifier_policy, str) or identifier_policy not in {
2505
+ "native_sets.v1", "strict_strings.v1"
2506
+ }:
2507
+ raise ValidationError("QA identifier_policy must be native_sets.v1 or strict_strings.v1")
2508
+ default_system_prompt = (
2509
+ "Answer using only the visible request and context. Return exactly one JSON "
2510
+ "object with keys answer, support_ids, evidence_ids, and evidence_texts. "
2511
+ "Each non-answer value must be an array of unique strings. Use the visible "
2512
+ "support identifiers when present; otherwise use visible block IDs. "
2513
+ "For 2Wiki evidence_ids, use canonical JSON strings of inferred "
2514
+ "[subject, relation, object] triples. Do not add markdown."
2515
+ if output_format == "structured_json"
2516
+ else (
2517
+ "Answer the question using only the visible request and context. "
2518
+ "Return only the answer text."
2519
+ )
2520
+ )
2521
+ system_prompt = qa.get("system_prompt", default_system_prompt)
2522
+ if not isinstance(system_prompt, str):
2523
+ raise ValidationError("QA system_prompt must be a string")
2524
+ prompt_revision = _require_text(
2525
+ qa.get("prompt_revision", "visible_qa_text.v1"),
2526
+ label="QA prompt_revision",
2527
+ )
2528
+ max_tokens = _positive_int(qa.get("max_tokens", 512), label="QA max_tokens")
2529
+ temperature = _nonnegative_number(qa.get("temperature", 0.0), label="QA temperature")
2530
+ stop = _text_sequence(qa.get("stop", ()), path="QA stop", allow_empty=True)
2531
+ use_seed = qa.get("use_seed", False)
2532
+ if not isinstance(use_seed, bool):
2533
+ raise ValidationError("QA use_seed must be boolean")
2534
+ required = _capabilities(
2535
+ qa.get("required_capabilities", (ModelCapability.COMPLETION.value,)),
2536
+ path="QA required_capabilities",
2537
+ )
2538
+ if not required:
2539
+ raise ValidationError("QA required_capabilities must not be empty")
2540
+
2541
+ retrieval = _strict_mapping(
2542
+ data.get("retrieval", {}),
2543
+ path="predictor parameters.retrieval",
2544
+ allowed={"top_k", "query_mode", "source_mode"},
2545
+ required=set(),
2546
+ )
2547
+ top_k = _positive_int(retrieval.get("top_k", 100), label="retrieval top_k")
2548
+ if top_k > 10_000:
2549
+ raise ValidationError("retrieval top_k must not exceed 10000")
2550
+ query_mode = retrieval.get("query_mode", "query_only")
2551
+ if query_mode not in {"query_only", "instruction_and_query"}:
2552
+ raise ValidationError("retrieval query_mode must be query_only or instruction_and_query")
2553
+ source_mode = retrieval.get("source_mode", "retriever")
2554
+ if source_mode not in {"retriever", "context_or_retriever"}:
2555
+ raise ValidationError("retrieval source_mode must be retriever or context_or_retriever")
2556
+ return MappingProxyType(
2557
+ {
2558
+ "schema_version": _SCHEMA_VERSION,
2559
+ "task": task,
2560
+ "qa": MappingProxyType(
2561
+ {
2562
+ "system_prompt": system_prompt,
2563
+ "prompt_revision": prompt_revision,
2564
+ "output_format": output_format,
2565
+ "structured_output_mode": structured_output_mode,
2566
+ "identifier_policy": identifier_policy,
2567
+ "max_tokens": max_tokens,
2568
+ "temperature": temperature,
2569
+ "stop": stop,
2570
+ "use_seed": use_seed,
2571
+ "required_capabilities": required,
2572
+ }
2573
+ ),
2574
+ "retrieval": MappingProxyType(
2575
+ {
2576
+ "top_k": top_k,
2577
+ "query_mode": query_mode,
2578
+ "source_mode": source_mode,
2579
+ }
2580
+ ),
2581
+ }
2582
+ )
2583
+
2584
+
2585
+ def _parse_corpus_retriever_parameters(parameters: Mapping[str, Any]) -> Mapping[str, Any]:
2586
+ _reject_embedded_secrets(parameters, path="corpus retriever parameters")
2587
+ data = _strict_mapping(
2588
+ parameters,
2589
+ path="corpus retriever parameters",
2590
+ allowed={
2591
+ "schema_version",
2592
+ "benchmark_id",
2593
+ "profile_id",
2594
+ "corpus_path",
2595
+ "corpus_sha256",
2596
+ "corpus_revision",
2597
+ "expected_document_count",
2598
+ "options",
2599
+ "artifact_files",
2600
+ },
2601
+ required={
2602
+ "schema_version",
2603
+ "benchmark_id",
2604
+ "profile_id",
2605
+ "corpus_path",
2606
+ "corpus_sha256",
2607
+ "corpus_revision",
2608
+ "expected_document_count",
2609
+ },
2610
+ )
2611
+ _require_schema_version(
2612
+ data["schema_version"],
2613
+ path="corpus retriever parameters.schema_version",
2614
+ )
2615
+ benchmark_id = _require_text(data["benchmark_id"], label="retriever benchmark_id")
2616
+ profile_id = _require_text(data["profile_id"], label="retriever profile_id")
2617
+ profile = retriever_profile(profile_id)
2618
+ options = _mapping_or_empty(
2619
+ data.get("options"),
2620
+ path="corpus retriever options",
2621
+ )
2622
+ if profile.backend == "memory_bm25":
2623
+ allowed_options = {"retriever_id", "k1", "b"}
2624
+ else:
2625
+ allowed_options = {
2626
+ "retriever_id",
2627
+ "batch_size",
2628
+ "device",
2629
+ "local_files_only",
2630
+ "model_path",
2631
+ }
2632
+ unknown_options = set(options) - allowed_options
2633
+ if unknown_options:
2634
+ raise ValidationError(
2635
+ "corpus retriever options contain unsupported fields: "
2636
+ + ", ".join(sorted(unknown_options))
2637
+ )
2638
+ normalized_options = dict(options)
2639
+ if profile.backend == "sentence_transformers_dense":
2640
+ if normalized_options.get("local_files_only", True) is not True:
2641
+ raise ValidationError("production dense retriever requires local_files_only=true")
2642
+ normalized_options["local_files_only"] = True
2643
+ if "model_path" in normalized_options:
2644
+ normalized_options["model_path"] = str(
2645
+ _absolute_path(normalized_options["model_path"], label="dense model_path")
2646
+ )
2647
+ return freeze_mapping(
2648
+ {
2649
+ "benchmark_id": benchmark_id,
2650
+ "profile_id": profile_id,
2651
+ "corpus_path": _absolute_path(data["corpus_path"], label="corpus_path"),
2652
+ "corpus_sha256": _sha256_text(
2653
+ data["corpus_sha256"],
2654
+ label="corpus_sha256",
2655
+ ),
2656
+ "corpus_revision": _require_text(
2657
+ data["corpus_revision"],
2658
+ label="corpus_revision",
2659
+ ),
2660
+ "expected_document_count": _positive_int(
2661
+ data["expected_document_count"],
2662
+ label="expected_document_count",
2663
+ ),
2664
+ "options": _canonical_mapping(
2665
+ normalized_options,
2666
+ label="corpus retriever options",
2667
+ ),
2668
+ "artifact_files": _artifact_file_locks(
2669
+ data.get("artifact_files", ()),
2670
+ path="corpus retriever artifact_files",
2671
+ ),
2672
+ }
2673
+ )
2674
+
2675
+
2676
+ def _load_replay_fixture(path: Path, expected_sha256: str) -> Mapping[str, Any]:
2677
+ raw = _read_checksum_bytes(
2678
+ path,
2679
+ expected_sha256,
2680
+ max_bytes=_MAX_REPLAY_BYTES,
2681
+ label="replay fixture",
2682
+ )
2683
+ parsed = _strict_json_bytes(raw, label="replay fixture")
2684
+ data = _strict_mapping(
2685
+ parsed,
2686
+ path="replay fixture",
2687
+ allowed={"schema_version", "responses", "strict_request_hashes"},
2688
+ required={"schema_version", "responses"},
2689
+ )
2690
+ _require_schema_version(data["schema_version"], path="replay fixture.schema_version")
2691
+ values = data["responses"]
2692
+ if isinstance(values, (str, bytes)) or not isinstance(values, Sequence) or not values:
2693
+ raise ArtifactError("replay fixture responses must be a non-empty array")
2694
+ responses: list[str | GenerationResponse] = []
2695
+ for index, value in enumerate(values):
2696
+ if isinstance(value, str):
2697
+ responses.append(value)
2698
+ continue
2699
+ item = _strict_mapping(
2700
+ value,
2701
+ path=f"replay fixture.responses[{index}]",
2702
+ allowed={"text", "model", "finish_reason", "usage", "raw"},
2703
+ required={"text"},
2704
+ )
2705
+ text = item["text"]
2706
+ if not isinstance(text, str):
2707
+ raise ArtifactError(f"replay fixture.responses[{index}].text must be a string")
2708
+ usage_data = _strict_mapping(
2709
+ item.get("usage", {}),
2710
+ path=f"replay fixture.responses[{index}].usage",
2711
+ allowed={"input_tokens", "output_tokens", "cost_usd"},
2712
+ required=set(),
2713
+ )
2714
+ try:
2715
+ usage = Usage(**usage_data)
2716
+ except (TypeError, ValueError) as exc:
2717
+ raise ArtifactError(
2718
+ f"replay fixture.responses[{index}].usage is invalid: {exc}"
2719
+ ) from exc
2720
+ responses.append(
2721
+ GenerationResponse(
2722
+ text=text,
2723
+ model=str(item.get("model", "")),
2724
+ finish_reason=str(item.get("finish_reason", "replay")),
2725
+ usage=usage,
2726
+ raw=_mapping_or_empty(
2727
+ item.get("raw"),
2728
+ path=f"replay fixture.responses[{index}].raw",
2729
+ ),
2730
+ )
2731
+ )
2732
+ hashes = _text_sequence(
2733
+ data.get("strict_request_hashes", ()),
2734
+ path="replay fixture.strict_request_hashes",
2735
+ allow_empty=True,
2736
+ )
2737
+ if hashes and len(hashes) != len(responses):
2738
+ raise ArtifactError("replay fixture strict_request_hashes must match response count")
2739
+ for value in hashes:
2740
+ _require_sha256(value, label="replay strict request hash")
2741
+ return MappingProxyType(
2742
+ {
2743
+ "responses": tuple(responses),
2744
+ "strict_request_hashes": hashes,
2745
+ }
2746
+ )
2747
+
2748
+
2749
+ def _load_locked_corpus(config: Mapping[str, Any]) -> tuple[Document, ...]:
2750
+ path = Path(config["corpus_path"])
2751
+ identity = _regular_file_identity(path, label="prepared corpus")
2752
+ return _load_locked_corpus_snapshot(
2753
+ str(path),
2754
+ config["corpus_sha256"],
2755
+ config["corpus_revision"],
2756
+ identity,
2757
+ )
2758
+
2759
+
2760
+ @lru_cache(maxsize=16)
2761
+ def _load_locked_corpus_snapshot(
2762
+ path_text: str,
2763
+ expected_sha256: str,
2764
+ corpus_revision: str,
2765
+ identity: tuple[int, int, int, int, int],
2766
+ ) -> tuple[Document, ...]:
2767
+ """Stream and cache one immutable corpus snapshot across arm runtimes."""
2768
+
2769
+ path = Path(path_text)
2770
+ digest = hashlib.sha256()
2771
+ documents: list[Document] = []
2772
+ identifiers: set[str] = set()
2773
+ try:
2774
+ with path.open("rb") as handle:
2775
+ if _stat_identity(os.fstat(handle.fileno())) != identity:
2776
+ raise ArtifactError("prepared corpus changed before it could be read")
2777
+ for line_number, raw_line in enumerate(handle, start=1):
2778
+ digest.update(raw_line)
2779
+ line = raw_line.rstrip(b"\r\n")
2780
+ if not line:
2781
+ raise ArtifactError(f"prepared corpus contains a blank line at {line_number}")
2782
+ if len(line) > _MAX_CORPUS_LINE_BYTES:
2783
+ raise ArtifactError(f"prepared corpus line {line_number} exceeds size limit")
2784
+ value = _strict_json_bytes(
2785
+ line,
2786
+ label=f"prepared corpus line {line_number}",
2787
+ )
2788
+ item = _strict_mapping(
2789
+ value,
2790
+ path=f"prepared corpus line {line_number}",
2791
+ allowed={"id", "title", "text"},
2792
+ required={"id", "title", "text"},
2793
+ )
2794
+ document_id = _require_text(
2795
+ item["id"],
2796
+ label=f"prepared corpus line {line_number}.id",
2797
+ )
2798
+ if document_id in identifiers:
2799
+ raise ArtifactError(f"prepared corpus contains duplicate ID {document_id!r}")
2800
+ identifiers.add(document_id)
2801
+ title = item["title"]
2802
+ text = item["text"]
2803
+ if not isinstance(title, str):
2804
+ raise ArtifactError(
2805
+ f"prepared corpus line {line_number}.title must be a string"
2806
+ )
2807
+ if not isinstance(text, str) or not text:
2808
+ raise ArtifactError(
2809
+ f"prepared corpus line {line_number}.text must be non-empty text"
2810
+ )
2811
+ documents.append(
2812
+ Document(
2813
+ doc_id=document_id,
2814
+ title=title,
2815
+ text=text,
2816
+ corpus_revision=corpus_revision,
2817
+ )
2818
+ )
2819
+ if _stat_identity(os.fstat(handle.fileno())) != identity:
2820
+ raise ArtifactError("prepared corpus changed while it was being read")
2821
+ except ArtifactError:
2822
+ raise
2823
+ except OSError as exc:
2824
+ raise ArtifactError(f"prepared corpus is unavailable: {path}") from exc
2825
+ if digest.hexdigest() != expected_sha256:
2826
+ raise ArtifactError(f"prepared corpus checksum mismatch: {path}")
2827
+ if not documents:
2828
+ raise ArtifactError("prepared corpus contains no documents")
2829
+ return tuple(documents)
2830
+
2831
+
2832
+ def _regular_file_identity(path: Path, *, label: str) -> tuple[int, int, int, int, int]:
2833
+ if not path.is_absolute():
2834
+ raise ArtifactError(f"{label} path must be absolute")
2835
+ try:
2836
+ metadata = path.stat()
2837
+ except OSError as exc:
2838
+ raise ArtifactError(f"{label} is unavailable: {path}") from exc
2839
+ if not stat.S_ISREG(metadata.st_mode):
2840
+ raise ArtifactError(f"{label} is not a regular file: {path}")
2841
+ return _stat_identity(metadata)
2842
+
2843
+
2844
+ def _stat_identity(metadata: os.stat_result) -> tuple[int, int, int, int, int]:
2845
+ return (
2846
+ metadata.st_dev,
2847
+ metadata.st_ino,
2848
+ metadata.st_size,
2849
+ metadata.st_mtime_ns,
2850
+ metadata.st_ctime_ns,
2851
+ )
2852
+
2853
+
2854
+ def _verify_checksum_file(
2855
+ path: str | Path,
2856
+ expected_sha256: str,
2857
+ *,
2858
+ label: str,
2859
+ ) -> None:
2860
+ source = Path(path)
2861
+ identity = _regular_file_identity(source, label=label)
2862
+ _require_sha256(expected_sha256, label=f"{label} SHA-256")
2863
+ observed = _hash_file_snapshot(str(source), identity)
2864
+ if observed != expected_sha256:
2865
+ raise ArtifactError(f"{label} checksum mismatch: {source}")
2866
+
2867
+
2868
+ def _hash_file_snapshot(
2869
+ path_text: str,
2870
+ identity: tuple[int, int, int, int, int],
2871
+ ) -> str:
2872
+ path = Path(path_text)
2873
+ digest = hashlib.sha256()
2874
+ try:
2875
+ with path.open("rb") as handle:
2876
+ if _stat_identity(os.fstat(handle.fileno())) != identity:
2877
+ raise ArtifactError(f"artifact changed before it could be read: {path}")
2878
+ for block in iter(lambda: handle.read(1024 * 1024), b""):
2879
+ digest.update(block)
2880
+ if _stat_identity(os.fstat(handle.fileno())) != identity:
2881
+ raise ArtifactError(f"artifact changed while it was being read: {path}")
2882
+ except ArtifactError:
2883
+ raise
2884
+ except OSError as exc:
2885
+ raise ArtifactError(f"artifact is unavailable: {path}") from exc
2886
+ return digest.hexdigest()
2887
+
2888
+
2889
+ def _render_visible_qa_prompt(state: FrozenAgentState) -> str:
2890
+ """Render only fields deliberately exposed by ``FrozenAgentState``.
2891
+
2892
+ ``task_id`` and ``metadata`` are intentionally omitted because either may
2893
+ carry benchmark identity. Evaluator gold is physically absent from this
2894
+ type and never enters the predictor API.
2895
+ """
2896
+
2897
+ sections: list[str] = []
2898
+ if state.instruction:
2899
+ sections.append(f"Instruction:\n{state.instruction}")
2900
+ if state.messages:
2901
+ conversation = "\n".join(f"{message.role}: {message.content}" for message in state.messages)
2902
+ sections.append(f"Conversation:\n{conversation}")
2903
+ sections.append(f"Question:\n{state.query}")
2904
+ if state.context:
2905
+ context = "\n\n".join(
2906
+ _render_visible_context_block(block, index=index)
2907
+ for index, block in enumerate(state.context, start=1)
2908
+ )
2909
+ sections.append(f"Visible context:\n{context}")
2910
+ if state.tool_names:
2911
+ sections.append("Available tool names:\n" + "\n".join(state.tool_names))
2912
+ return "\n\n".join(sections)
2913
+
2914
+
2915
+ def _qa_response_format(
2916
+ *,
2917
+ benchmark_id: str,
2918
+ output_format: str,
2919
+ structured_output_mode: str,
2920
+ ) -> Mapping[str, Any] | None:
2921
+ if output_format == "answer_only":
2922
+ return None
2923
+ if structured_output_mode == "json_object":
2924
+ return {"type": "json_object"}
2925
+ if structured_output_mode != "json_schema": # pragma: no cover - parsed invariant
2926
+ raise ValidationError("unsupported structured QA output mode")
2927
+ identifier_limit = 0 if benchmark_id == "qasper.v0_3_dev" else 16
2928
+ evidence_text_limit = 3 if benchmark_id == "qasper.v0_3_dev" else 16
2929
+ schema = {
2930
+ "type": "object",
2931
+ "additionalProperties": False,
2932
+ "required": ["answer", "support_ids", "evidence_ids", "evidence_texts"],
2933
+ "properties": {
2934
+ "answer": {"type": "string", "minLength": 1},
2935
+ "support_ids": {
2936
+ "type": "array",
2937
+ "maxItems": identifier_limit,
2938
+ "items": {"type": "string", "minLength": 1},
2939
+ },
2940
+ "evidence_ids": {
2941
+ "type": "array",
2942
+ "maxItems": identifier_limit,
2943
+ "items": {"type": "string", "minLength": 1},
2944
+ },
2945
+ "evidence_texts": {
2946
+ "type": "array",
2947
+ "maxItems": evidence_text_limit,
2948
+ "items": {"type": "string", "minLength": 1},
2949
+ },
2950
+ },
2951
+ }
2952
+ return {
2953
+ "type": "json_schema",
2954
+ "json_schema": {
2955
+ "name": "qa_prediction",
2956
+ "strict": True,
2957
+ "schema": schema,
2958
+ },
2959
+ }
2960
+
2961
+
2962
+ def _render_visible_context_block(block: Any, *, index: int) -> str:
2963
+ support_id = block.metadata.get("official_support_id", block.block_id)
2964
+ source = f"\nSource: {block.source}" if block.source else ""
2965
+ return (
2966
+ f"[Context {index}]\nBlock ID: {block.block_id}\n"
2967
+ f"Support ID: {support_id}{source}\nText: {block.text}"
2968
+ )
2969
+
2970
+
2971
+ def _parse_visible_qa_prediction(
2972
+ text: str,
2973
+ *,
2974
+ benchmark_id: str,
2975
+ state: FrozenAgentState,
2976
+ output_format: str,
2977
+ identifier_policy: str = "native_sets.v1",
2978
+ ) -> tuple[QAPrediction, Mapping[str, Any]]:
2979
+ if not isinstance(identifier_policy, str) or identifier_policy not in {
2980
+ "native_sets.v1", "strict_strings.v1"
2981
+ }:
2982
+ raise ValidationError("unsupported QA identifier policy")
2983
+ if output_format == "answer_only":
2984
+ return QAPrediction(answer=text.strip()), {
2985
+ "identifier_policy": identifier_policy,
2986
+ "support_id_validation": "not_applicable_answer_only.v1",
2987
+ "valid_support_id_count": 0,
2988
+ "invalid_support_id_count": 0,
2989
+ "invalid_support_ids_sha256": None,
2990
+ "evidence_id_validation": "not_applicable_answer_only.v1",
2991
+ "valid_evidence_id_count": 0,
2992
+ "invalid_evidence_id_count": 0,
2993
+ "invalid_evidence_ids_sha256": None,
2994
+ }
2995
+ if output_format != "structured_json": # pragma: no cover - parsed config invariant
2996
+ raise ValidationError("unsupported QA output format")
2997
+ try:
2998
+ value = json.loads(
2999
+ text,
3000
+ object_pairs_hook=_reject_duplicate_keys,
3001
+ parse_constant=lambda item: _reject_json_constant(item),
3002
+ )
3003
+ except (TypeError, json.JSONDecodeError) as exc:
3004
+ raise ValidationError("QA backend response is not strict JSON") from exc
3005
+ if not isinstance(value, Mapping):
3006
+ raise ValidationError("structured QA response must be a JSON object")
3007
+ expected = {"answer", "support_ids", "evidence_ids", "evidence_texts"}
3008
+ if set(value) != expected:
3009
+ raise ValidationError(
3010
+ "structured QA response schema mismatch: "
3011
+ f"missing={sorted(expected - set(value))}, "
3012
+ f"unknown={sorted(set(value) - expected)}"
3013
+ )
3014
+ answer = value["answer"]
3015
+ if not isinstance(answer, str) or not answer.strip():
3016
+ raise ValidationError("structured QA answer must be non-empty text")
3017
+ identifier_audit: dict[str, Any] = {"identifier_policy": identifier_policy}
3018
+ malformed_support_ids: tuple[Any, ...] = ()
3019
+ malformed_evidence_ids: tuple[Any, ...] = ()
3020
+ if identifier_policy == "strict_strings.v1":
3021
+ support_ids = _prediction_string_array(
3022
+ value["support_ids"], label="structured QA support_ids"
3023
+ )
3024
+ evidence_ids = _prediction_string_array(
3025
+ value["evidence_ids"], label="structured QA evidence_ids"
3026
+ )
3027
+ else:
3028
+ support_ids, malformed_support_ids, support_audit = _normalize_qa_identifier_array(
3029
+ value["support_ids"],
3030
+ label="structured QA support_ids",
3031
+ kind=(
3032
+ "support_pair"
3033
+ if benchmark_id in {"2wiki.multihop", "hotpotqa.distractor"}
3034
+ else "opaque"
3035
+ ),
3036
+ )
3037
+ evidence_ids, malformed_evidence_ids, evidence_audit = _normalize_qa_identifier_array(
3038
+ value["evidence_ids"],
3039
+ label="structured QA evidence_ids",
3040
+ kind="evidence_triple" if benchmark_id == "2wiki.multihop" else "opaque",
3041
+ )
3042
+ identifier_audit.update(
3043
+ {f"support_{key}": item for key, item in support_audit.items()}
3044
+ )
3045
+ identifier_audit.update(
3046
+ {f"evidence_{key}": item for key, item in evidence_audit.items()}
3047
+ )
3048
+ evidence_texts, duplicate_evidence_texts = _prediction_string_array_keep_first(
3049
+ value["evidence_texts"],
3050
+ label="structured QA evidence_texts",
3051
+ )
3052
+ visible_support_ids = {
3053
+ str(block.metadata.get("official_support_id", block.block_id)) for block in state.context
3054
+ }
3055
+ valid_support_ids, invalid_support_ids = _partition_visible_support_ids(
3056
+ support_ids,
3057
+ visible_support_ids=visible_support_ids,
3058
+ benchmark_id=benchmark_id,
3059
+ )
3060
+ all_invalid_support_ids = (*malformed_support_ids, *invalid_support_ids)
3061
+ support_validation = (
3062
+ "filter_unknown_or_invalid_canonical_support.v1"
3063
+ if benchmark_id in {"2wiki.multihop", "hotpotqa.distractor"}
3064
+ else "filter_unknown_visible_only.v1"
3065
+ )
3066
+ valid_evidence_ids = evidence_ids
3067
+ invalid_evidence_ids: tuple[str, ...] = ()
3068
+ evidence_validation = "not_applicable_non_2wiki.v1"
3069
+ if benchmark_id == "2wiki.multihop":
3070
+ valid_evidence_ids, invalid_evidence_ids = _partition_two_wiki_evidence_ids(evidence_ids)
3071
+ evidence_validation = "filter_invalid_2wiki_canonical_triples.v1"
3072
+ all_invalid_evidence_ids = (*malformed_evidence_ids, *invalid_evidence_ids)
3073
+ return (
3074
+ QAPrediction(
3075
+ answer=answer.strip(),
3076
+ support_ids=valid_support_ids,
3077
+ evidence_ids=valid_evidence_ids,
3078
+ evidence_texts=evidence_texts,
3079
+ ),
3080
+ {
3081
+ **identifier_audit,
3082
+ "support_id_validation": support_validation,
3083
+ "valid_support_id_count": len(valid_support_ids),
3084
+ "invalid_support_id_count": len(all_invalid_support_ids),
3085
+ "invalid_support_ids_sha256": (
3086
+ content_hash(all_invalid_support_ids) if all_invalid_support_ids else None
3087
+ ),
3088
+ "evidence_id_validation": evidence_validation,
3089
+ "valid_evidence_id_count": len(valid_evidence_ids),
3090
+ "invalid_evidence_id_count": len(all_invalid_evidence_ids),
3091
+ "invalid_evidence_ids_sha256": (
3092
+ content_hash(all_invalid_evidence_ids) if all_invalid_evidence_ids else None
3093
+ ),
3094
+ "evidence_text_duplicate_policy": "deduplicate_exact_trimmed_keep_first.v1",
3095
+ "duplicate_evidence_text_count": len(duplicate_evidence_texts),
3096
+ "duplicate_evidence_texts_sha256": (
3097
+ content_hash(duplicate_evidence_texts) if duplicate_evidence_texts else None
3098
+ ),
3099
+ },
3100
+ )
3101
+
3102
+
3103
+ def _normalize_qa_identifier_array(
3104
+ value: Any,
3105
+ *,
3106
+ label: str,
3107
+ kind: str,
3108
+ ) -> tuple[tuple[str, ...], tuple[Any, ...], Mapping[str, Any]]:
3109
+ """Normalize set-like IDs without inferring evidence or reading references.
3110
+
3111
+ Native support pairs may be JSON strings, nested pairs, or an unambiguous
3112
+ flat title/integer stream. Native 2Wiki triples must be JSON strings or
3113
+ nested triples: a flat string stream is ambiguous and is never grouped.
3114
+ Unknown IDs still pass through the visible-support filter. Invalid typed
3115
+ entries are excluded and hash-audited; no coercion or title-only lookup is
3116
+ permitted. All policies keep strict JSON and the required response fields.
3117
+ """
3118
+
3119
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
3120
+ raise ValidationError(f"{label} must be an array")
3121
+ identifiers: list[str] = []
3122
+ invalid: list[Any] = []
3123
+ duplicates: list[str] = []
3124
+ seen: set[str] = set()
3125
+ normalized_count = 0
3126
+ index = 0
3127
+ while index < len(value):
3128
+ raw = value[index]
3129
+ index += 1
3130
+ # Only a non-boolean, non-negative integer makes this shape unambiguous.
3131
+ # Never turn e.g. [title, true], [title, "0"], or title alone into an ID.
3132
+ if (
3133
+ kind == "support_pair"
3134
+ and isinstance(raw, str)
3135
+ and raw.strip()
3136
+ and index < len(value)
3137
+ and isinstance(value[index], int)
3138
+ and not isinstance(value[index], bool)
3139
+ and value[index] >= 0
3140
+ ):
3141
+ raw = [raw, value[index]]
3142
+ index += 1
3143
+ if isinstance(raw, str) and raw.strip():
3144
+ identifier = raw.strip()
3145
+ if kind != "opaque":
3146
+ try:
3147
+ native = json.loads(identifier)
3148
+ except json.JSONDecodeError:
3149
+ native = None
3150
+ if _is_native_qa_identifier(native, kind=kind):
3151
+ identifier = canonical_json(native)
3152
+ elif _is_native_qa_identifier(raw, kind=kind):
3153
+ identifier = canonical_json(raw)
3154
+ else:
3155
+ invalid.append(raw)
3156
+ continue
3157
+ if not isinstance(raw, str) or identifier != raw:
3158
+ normalized_count += 1
3159
+ if identifier in seen:
3160
+ duplicates.append(identifier)
3161
+ continue
3162
+ seen.add(identifier)
3163
+ identifiers.append(identifier)
3164
+ return (
3165
+ tuple(identifiers),
3166
+ tuple(invalid),
3167
+ {
3168
+ "raw_identifier_array_sha256": content_hash(value),
3169
+ "normalized_id_count": normalized_count,
3170
+ "duplicate_id_count": len(duplicates),
3171
+ "duplicate_ids_sha256": content_hash(duplicates) if duplicates else None,
3172
+ },
3173
+ )
3174
+
3175
+
3176
+ def _is_native_qa_identifier(value: Any, *, kind: str) -> bool:
3177
+ if not isinstance(value, list):
3178
+ return False
3179
+ if kind == "support_pair":
3180
+ return (
3181
+ len(value) == 2
3182
+ and isinstance(value[0], str)
3183
+ and bool(value[0].strip())
3184
+ and isinstance(value[1], int)
3185
+ and not isinstance(value[1], bool)
3186
+ and value[1] >= 0
3187
+ )
3188
+ return (
3189
+ kind == "evidence_triple"
3190
+ and len(value) == 3
3191
+ and all(isinstance(item, str) and item.strip() for item in value)
3192
+ )
3193
+
3194
+
3195
+ def _partition_visible_support_ids(
3196
+ identifiers: tuple[str, ...],
3197
+ *,
3198
+ visible_support_ids: set[str],
3199
+ benchmark_id: str,
3200
+ ) -> tuple[tuple[str, ...], tuple[str, ...]]:
3201
+ require_canonical_pair = benchmark_id in {
3202
+ "2wiki.multihop",
3203
+ "hotpotqa.distractor",
3204
+ }
3205
+ valid: list[str] = []
3206
+ invalid: list[str] = []
3207
+ for identifier in identifiers:
3208
+ if identifier not in visible_support_ids or (
3209
+ require_canonical_pair and not _is_canonical_support_pair(identifier)
3210
+ ):
3211
+ invalid.append(identifier)
3212
+ else:
3213
+ valid.append(identifier)
3214
+ return tuple(valid), tuple(invalid)
3215
+
3216
+
3217
+ def _is_canonical_support_pair(identifier: str) -> bool:
3218
+ try:
3219
+ value = json.loads(identifier)
3220
+ except json.JSONDecodeError:
3221
+ return False
3222
+ return bool(
3223
+ isinstance(value, list)
3224
+ and len(value) == 2
3225
+ and isinstance(value[0], str)
3226
+ and value[0].strip()
3227
+ and isinstance(value[1], int)
3228
+ and not isinstance(value[1], bool)
3229
+ and value[1] >= 0
3230
+ and canonical_json(value) == identifier
3231
+ )
3232
+
3233
+
3234
+ def _partition_two_wiki_evidence_ids(
3235
+ identifiers: tuple[str, ...],
3236
+ ) -> tuple[tuple[str, ...], tuple[str, ...]]:
3237
+ valid: list[str] = []
3238
+ invalid: list[str] = []
3239
+ for identifier in identifiers:
3240
+ try:
3241
+ value = json.loads(identifier)
3242
+ except json.JSONDecodeError:
3243
+ invalid.append(identifier)
3244
+ continue
3245
+ if (
3246
+ not isinstance(value, list)
3247
+ or len(value) != 3
3248
+ or not all(isinstance(item, str) and item.strip() for item in value)
3249
+ or canonical_json(value) != identifier
3250
+ ):
3251
+ invalid.append(identifier)
3252
+ continue
3253
+ valid.append(identifier)
3254
+ return tuple(valid), tuple(invalid)
3255
+
3256
+
3257
+ def _prediction_string_array(value: Any, *, label: str) -> tuple[str, ...]:
3258
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
3259
+ raise ValidationError(f"{label} must be an array")
3260
+ items = tuple(value)
3261
+ if any(not isinstance(item, str) or not item.strip() for item in items):
3262
+ raise ValidationError(f"{label} must contain non-empty strings")
3263
+ normalized = tuple(item.strip() for item in items)
3264
+ if len(normalized) != len(set(normalized)):
3265
+ raise ValidationError(f"{label} must not contain duplicates")
3266
+ return normalized
3267
+
3268
+
3269
+ def _prediction_string_array_keep_first(
3270
+ value: Any,
3271
+ *,
3272
+ label: str,
3273
+ ) -> tuple[tuple[str, ...], tuple[str, ...]]:
3274
+ """Normalize a model-produced set-like array and audit exact repeats.
3275
+
3276
+ Evidence-text scoring is set based, so an otherwise valid model response
3277
+ must not abort a run solely because it repeated the same visible span.
3278
+ The first occurrence is retained, subsequent exact trimmed repeats are
3279
+ returned for hash-only audit metadata, and malformed entries still fail
3280
+ closed.
3281
+ """
3282
+
3283
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
3284
+ raise ValidationError(f"{label} must be an array")
3285
+ items = tuple(value)
3286
+ if any(not isinstance(item, str) or not item.strip() for item in items):
3287
+ raise ValidationError(f"{label} must contain non-empty strings")
3288
+ unique: list[str] = []
3289
+ duplicates: list[str] = []
3290
+ seen: set[str] = set()
3291
+ for item in items:
3292
+ normalized = item.strip()
3293
+ if normalized in seen:
3294
+ duplicates.append(normalized)
3295
+ continue
3296
+ seen.add(normalized)
3297
+ unique.append(normalized)
3298
+ return tuple(unique), tuple(duplicates)
3299
+
3300
+
3301
+ def _resolve_retriever_builder(
3302
+ spec: LockedRetrieverSpec,
3303
+ ) -> ProductionRetrieverBuilderProtocol:
3304
+ candidate = resolve_driver_entrypoint(spec.entrypoint)
3305
+ if not callable(candidate):
3306
+ raise ArtifactError("retriever builder entrypoint must be a zero-argument factory")
3307
+ try:
3308
+ builder = candidate()
3309
+ except TypeError as exc:
3310
+ raise ArtifactError("retriever builder entrypoint must be a zero-argument factory") from exc
3311
+ if not isinstance(builder, ProductionRetrieverBuilderProtocol):
3312
+ raise ArtifactError("retriever builder does not implement the production protocol")
3313
+ if builder.builder_kind != "retriever":
3314
+ raise ArtifactError("nested production builder kind must be 'retriever'")
3315
+ observed = builder_fingerprint_sha256(builder)
3316
+ if observed != spec.fingerprint_sha256:
3317
+ raise ArtifactError("retriever builder fingerprint checksum mismatch")
3318
+ return builder
3319
+
3320
+
3321
+ def _fingerprint(value: Any, *, label: str) -> Mapping[str, Any]:
3322
+ fingerprint = getattr(value, "fingerprint", None)
3323
+ if not callable(fingerprint):
3324
+ raise ValidationError(f"{label} must expose fingerprint()")
3325
+ identity = fingerprint()
3326
+ if not isinstance(identity, Mapping) or not identity:
3327
+ raise ValidationError(f"{label} fingerprint must be a non-empty mapping")
3328
+ return _canonical_mapping(identity, label=f"{label} fingerprint")
3329
+
3330
+
3331
+ def _validate_product_hash(observed: str, expected: str, *, label: str) -> str:
3332
+ _require_sha256(observed, label=f"{label} observed SHA-256")
3333
+ _require_sha256(expected, label=f"{label} expected SHA-256")
3334
+ if observed != expected:
3335
+ raise ArtifactError(f"{label} checksum mismatch")
3336
+ return observed
3337
+
3338
+
3339
+ def _artifact_file_locks(value: Any, *, path: str) -> tuple[Mapping[str, Any], ...]:
3340
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
3341
+ raise ValidationError(f"{path} must be an array")
3342
+ locks: list[Mapping[str, Any]] = []
3343
+ names: set[str] = set()
3344
+ paths: set[Path] = set()
3345
+ for index, item in enumerate(value):
3346
+ data = _strict_mapping(
3347
+ item,
3348
+ path=f"{path}[{index}]",
3349
+ allowed={"name", "path", "sha256"},
3350
+ required={"name", "path", "sha256"},
3351
+ )
3352
+ name = _require_text(data["name"], label=f"{path}[{index}].name")
3353
+ artifact_path = _absolute_path(data["path"], label=f"{path}[{index}].path")
3354
+ digest = _sha256_text(data["sha256"], label=f"{path}[{index}].sha256")
3355
+ if name in names or artifact_path in paths:
3356
+ raise ValidationError(f"{path} contains duplicate names or paths")
3357
+ names.add(name)
3358
+ paths.add(artifact_path)
3359
+ locks.append(
3360
+ freeze_mapping(
3361
+ {
3362
+ "name": name,
3363
+ "path": artifact_path,
3364
+ "sha256": digest,
3365
+ }
3366
+ )
3367
+ )
3368
+ return tuple(locks)
3369
+
3370
+
3371
+ def _validate_transformers_artifact_layout(
3372
+ model_path: Path,
3373
+ artifacts: Sequence[Mapping[str, Any]],
3374
+ ) -> None:
3375
+ """Require artifact names to be exact paths within the local snapshot."""
3376
+
3377
+ for artifact in artifacts:
3378
+ artifact_path = Path(artifact["path"])
3379
+ try:
3380
+ relative = artifact_path.relative_to(model_path).as_posix()
3381
+ except ValueError as exc:
3382
+ raise ValidationError("Transformers artifact paths must be inside model_path") from exc
3383
+ if not relative or relative == ".":
3384
+ raise ValidationError("Transformers artifacts must identify files")
3385
+ if artifact["name"] != relative:
3386
+ raise ValidationError(
3387
+ "Transformers artifact names must equal their model-relative POSIX paths"
3388
+ )
3389
+
3390
+
3391
+ def _transformers_snapshot_sha256(
3392
+ artifacts: Sequence[Mapping[str, Any]],
3393
+ ) -> str:
3394
+ return content_hash(
3395
+ {
3396
+ "protocol": "agentinputlab.transformers-snapshot-tree.v1",
3397
+ "files": tuple(
3398
+ {
3399
+ "path": artifact["name"],
3400
+ "sha256": artifact["sha256"],
3401
+ }
3402
+ for artifact in sorted(artifacts, key=lambda item: item["name"])
3403
+ ),
3404
+ }
3405
+ )
3406
+
3407
+
3408
+ def _verify_transformers_snapshot(
3409
+ model_path: Path,
3410
+ artifacts: Sequence[Mapping[str, Any]],
3411
+ ) -> None:
3412
+ """Verify the complete, closed model tree and every declared file digest."""
3413
+
3414
+ _validate_transformers_artifact_layout(model_path, artifacts)
3415
+ try:
3416
+ root_metadata = model_path.lstat()
3417
+ except OSError as exc:
3418
+ raise ArtifactError(f"Transformers model snapshot is unavailable: {model_path}") from exc
3419
+ if stat.S_ISLNK(root_metadata.st_mode) or not stat.S_ISDIR(root_metadata.st_mode):
3420
+ raise ArtifactError(f"Transformers model_path is not a closed directory: {model_path}")
3421
+
3422
+ observed_paths: set[Path] = set()
3423
+ try:
3424
+ for candidate in model_path.rglob("*"):
3425
+ metadata = candidate.lstat()
3426
+ if stat.S_ISLNK(metadata.st_mode):
3427
+ raise ArtifactError(
3428
+ "Transformers model snapshot contains a symlink: "
3429
+ f"{candidate.relative_to(model_path)}"
3430
+ )
3431
+ if stat.S_ISDIR(metadata.st_mode):
3432
+ continue
3433
+ if not stat.S_ISREG(metadata.st_mode):
3434
+ raise ArtifactError(
3435
+ "Transformers model snapshot contains a non-regular entry: "
3436
+ f"{candidate.relative_to(model_path)}"
3437
+ )
3438
+ observed_paths.add(candidate.resolve())
3439
+ except ArtifactError:
3440
+ raise
3441
+ except OSError as exc:
3442
+ raise ArtifactError(
3443
+ f"Transformers model snapshot cannot be enumerated: {model_path}"
3444
+ ) from exc
3445
+
3446
+ expected_paths = {Path(artifact["path"]) for artifact in artifacts}
3447
+ if observed_paths != expected_paths:
3448
+ missing = sorted(
3449
+ path.relative_to(model_path).as_posix() for path in observed_paths - expected_paths
3450
+ )
3451
+ stale = sorted(
3452
+ path.relative_to(model_path).as_posix() for path in expected_paths - observed_paths
3453
+ )
3454
+ raise ArtifactError(
3455
+ "Transformers artifact_files do not cover the complete model snapshot; "
3456
+ f"unlisted={missing[:5]}, absent={stale[:5]}"
3457
+ )
3458
+ for artifact in artifacts:
3459
+ _verify_checksum_file(
3460
+ artifact["path"],
3461
+ artifact["sha256"],
3462
+ label=f"Transformers artifact {artifact['name']!r}",
3463
+ )
3464
+
3465
+
3466
+ def _read_checksum_bytes(
3467
+ path: str | Path,
3468
+ expected_sha256: str,
3469
+ *,
3470
+ max_bytes: int | None,
3471
+ label: str,
3472
+ ) -> bytes:
3473
+ path = Path(path)
3474
+ if not path.is_absolute():
3475
+ raise ArtifactError(f"{label} path must be absolute")
3476
+ _require_sha256(expected_sha256, label=f"{label} SHA-256")
3477
+ try:
3478
+ size = path.stat().st_size
3479
+ if not path.is_file():
3480
+ raise ArtifactError(f"{label} is not a regular file: {path}")
3481
+ if max_bytes is not None and size > max_bytes:
3482
+ raise ArtifactError(f"{label} exceeds the size limit: {path}")
3483
+ raw = path.read_bytes()
3484
+ except ArtifactError:
3485
+ raise
3486
+ except OSError as exc:
3487
+ raise ArtifactError(f"{label} is unavailable: {path}") from exc
3488
+ observed = hashlib.sha256(raw).hexdigest()
3489
+ if observed != expected_sha256:
3490
+ raise ArtifactError(f"{label} checksum mismatch: {path}")
3491
+ return raw
3492
+
3493
+
3494
+ def _strict_json_bytes(raw: bytes, *, label: str) -> Any:
3495
+ try:
3496
+ return json.loads(
3497
+ raw.decode("utf-8"),
3498
+ object_pairs_hook=_reject_duplicate_keys,
3499
+ parse_constant=lambda value: _reject_json_constant(value),
3500
+ )
3501
+ except (UnicodeError, json.JSONDecodeError) as exc:
3502
+ raise ArtifactError(f"{label} is not strict UTF-8 JSON") from exc
3503
+
3504
+
3505
+ def _reject_duplicate_keys(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
3506
+ output: dict[str, Any] = {}
3507
+ for key, value in pairs:
3508
+ if key in output:
3509
+ raise ValidationError(f"duplicate JSON key: {key}")
3510
+ output[key] = value
3511
+ return output
3512
+
3513
+
3514
+ def _reject_json_constant(value: str) -> None:
3515
+ raise ValidationError(f"non-finite JSON constant is forbidden: {value}")
3516
+
3517
+
3518
+ def _reject_embedded_secrets(value: Any, *, path: str) -> None:
3519
+ if isinstance(value, Mapping):
3520
+ for key, item in value.items():
3521
+ if not isinstance(key, str):
3522
+ raise ValidationError(f"{path} keys must be strings")
3523
+ child = f"{path}.{key}"
3524
+ if key == "require_api_key" or path.endswith(".extra_headers"):
3525
+ _reject_embedded_secrets(item, path=child)
3526
+ continue
3527
+ # OpenAI tool declarations use arbitrary domain field names as keys
3528
+ # below JSON Schema ``properties``. A field such as
3529
+ # ``new_password`` names an argument; it is not itself a credential.
3530
+ # Keep recursively scanning the property's schema so secret-like
3531
+ # defaults, examples, descriptions, and other literal values still
3532
+ # fail closed.
3533
+ if (
3534
+ path.startswith("backend parameters.tools.definitions[")
3535
+ and ".function.parameters" in path
3536
+ and path.endswith(".properties")
3537
+ ):
3538
+ _reject_embedded_secrets(item, path=child)
3539
+ continue
3540
+ if _SENSITIVE_KEY.search(key):
3541
+ if not key.casefold().endswith("_env"):
3542
+ raise ValidationError(
3543
+ f"{child} looks secret-bearing; store only an environment-variable name"
3544
+ )
3545
+ _require_env_name(item, label=child)
3546
+ continue
3547
+ _reject_embedded_secrets(item, path=child)
3548
+ return
3549
+ if isinstance(value, (tuple, list)):
3550
+ for index, item in enumerate(value):
3551
+ _reject_embedded_secrets(item, path=f"{path}[{index}]")
3552
+ return
3553
+ if isinstance(value, str) and any(pattern.search(value) for pattern in _SECRET_VALUE_PATTERNS):
3554
+ raise ValidationError(
3555
+ f"{path} contains secret-like text; use environment-variable indirection"
3556
+ )
3557
+
3558
+
3559
+ def _capabilities(value: Any, *, path: str) -> tuple[ModelCapability, ...]:
3560
+ names = _text_sequence(value, path=path, allow_empty=True)
3561
+ try:
3562
+ return tuple(sorted((ModelCapability(name) for name in names), key=lambda item: item.value))
3563
+ except ValueError as exc:
3564
+ raise ValidationError(f"{path} contains an unknown capability") from exc
3565
+
3566
+
3567
+ def _environment_names(value: Any, *, path: str) -> tuple[str, ...]:
3568
+ names = _text_sequence(value, path=path, allow_empty=True)
3569
+ for name in names:
3570
+ _require_env_name(name, label=path)
3571
+ return tuple(sorted(names))
3572
+
3573
+
3574
+ def _text_sequence(value: Any, *, path: str, allow_empty: bool) -> tuple[str, ...]:
3575
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
3576
+ raise ValidationError(f"{path} must be an array of strings")
3577
+ items = tuple(value)
3578
+ if not allow_empty and not items:
3579
+ raise ValidationError(f"{path} must not be empty")
3580
+ if any(not isinstance(item, str) or not item for item in items):
3581
+ raise ValidationError(f"{path} must contain non-empty strings")
3582
+ if len(set(items)) != len(items):
3583
+ raise ValidationError(f"{path} must not contain duplicates")
3584
+ return items
3585
+
3586
+
3587
+ def _strict_mapping(
3588
+ value: Any,
3589
+ *,
3590
+ path: str,
3591
+ allowed: set[str],
3592
+ required: set[str],
3593
+ ) -> Mapping[str, Any]:
3594
+ if not isinstance(value, Mapping):
3595
+ raise ValidationError(f"{path} must be an object")
3596
+ if any(not isinstance(key, str) for key in value):
3597
+ raise ValidationError(f"{path} keys must be strings")
3598
+ unknown = set(value) - allowed
3599
+ missing = required - set(value)
3600
+ if unknown:
3601
+ raise ValidationError(f"{path} has unknown fields: {', '.join(sorted(unknown))}")
3602
+ if missing:
3603
+ raise ValidationError(f"{path} is missing fields: {', '.join(sorted(missing))}")
3604
+ return value
3605
+
3606
+
3607
+ def _mapping_or_empty(value: Any, *, path: str) -> Mapping[str, Any]:
3608
+ if value is None:
3609
+ return {}
3610
+ if not isinstance(value, Mapping):
3611
+ raise ValidationError(f"{path} must be an object")
3612
+ return value
3613
+
3614
+
3615
+ def _canonical_mapping(value: Mapping[str, Any], *, label: str) -> Mapping[str, Any]:
3616
+ try:
3617
+ return freeze_mapping(value)
3618
+ except (TypeError, ValueError) as exc:
3619
+ raise ValidationError(f"{label} must be canonical JSON") from exc
3620
+
3621
+
3622
+ def _require_schema_version(value: Any, *, path: str) -> None:
3623
+ if value != _SCHEMA_VERSION or isinstance(value, bool):
3624
+ raise ValidationError(f"{path} must equal {_SCHEMA_VERSION}")
3625
+
3626
+
3627
+ def _require_cell(value: str) -> None:
3628
+ if value not in _CELL_SPLITS:
3629
+ raise ValidationError(
3630
+ f"unsupported static production benchmark {value!r}; "
3631
+ f"available: {', '.join(sorted(_CELL_SPLITS))}"
3632
+ )
3633
+
3634
+
3635
+ def _require_retriever_cell(value: str) -> None:
3636
+ if value not in _RETRIEVER_ROUTE_CELLS:
3637
+ raise ValidationError(
3638
+ f"unsupported production retriever benchmark {value!r}; "
3639
+ f"available: {', '.join(sorted(_RETRIEVER_ROUTE_CELLS))}"
3640
+ )
3641
+
3642
+
3643
+ def _require_text(value: Any, *, label: str) -> str:
3644
+ if not isinstance(value, str) or not value.strip():
3645
+ raise ValidationError(f"{label} must be a non-empty string")
3646
+ return value
3647
+
3648
+
3649
+ def _verify_locked_file(path: Path, expected_sha256: str, *, label: str) -> str:
3650
+ if not path.is_file():
3651
+ raise ArtifactError(f"{label} is unavailable: {path}")
3652
+ observed = hashlib.sha256(path.read_bytes()).hexdigest()
3653
+ if observed != expected_sha256:
3654
+ raise ArtifactError(f"{label} checksum mismatch")
3655
+ return observed
3656
+
3657
+
3658
+ def _absolute_path(value: Any, *, label: str) -> Path:
3659
+ text = _require_text(value, label=label)
3660
+ path = Path(text).expanduser()
3661
+ if not path.is_absolute():
3662
+ raise ValidationError(f"{label} must be an absolute path")
3663
+ return path.resolve()
3664
+
3665
+
3666
+ def _absolute_http_url(value: Any, *, label: str) -> str:
3667
+ text = _require_text(value, label=label)
3668
+ parsed = urllib.parse.urlparse(text)
3669
+ if parsed.scheme not in {"http", "https"} or not parsed.netloc:
3670
+ raise ValidationError(f"{label} must be an absolute http(s) URL")
3671
+ if parsed.username or parsed.password or parsed.query or parsed.fragment:
3672
+ raise ValidationError(f"{label} must not contain credentials, query, or fragment")
3673
+ return text.rstrip("/")
3674
+
3675
+
3676
+ def _sha256_text(value: Any, *, label: str) -> str:
3677
+ text = _require_text(value, label=label)
3678
+ _require_sha256(text, label=label)
3679
+ return text
3680
+
3681
+
3682
+ def _require_sha256(value: Any, *, label: str) -> None:
3683
+ if not isinstance(value, str) or _SHA256.fullmatch(value) is None:
3684
+ raise ValidationError(f"{label} must be a lowercase SHA-256")
3685
+
3686
+
3687
+ def _require_env_name(value: Any, *, label: str) -> str:
3688
+ if not isinstance(value, str) or _ENV_NAME.fullmatch(value) is None:
3689
+ raise ValidationError(f"{label} must be an environment-variable name")
3690
+ return value
3691
+
3692
+
3693
+ def _require_seed(value: Any) -> None:
3694
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
3695
+ raise ValidationError("seed must be a non-negative integer")
3696
+
3697
+
3698
+ def _positive_int(value: Any, *, label: str) -> int:
3699
+ if isinstance(value, bool) or not isinstance(value, int) or value < 1:
3700
+ raise ValidationError(f"{label} must be a positive integer")
3701
+ return value
3702
+
3703
+
3704
+ def _positive_number(value: Any, *, label: str) -> float:
3705
+ number = _nonnegative_number(value, label=label)
3706
+ if number <= 0:
3707
+ raise ValidationError(f"{label} must be positive")
3708
+ return number
3709
+
3710
+
3711
+ def _nonnegative_number(value: Any, *, label: str) -> float:
3712
+ if (
3713
+ isinstance(value, bool)
3714
+ or not isinstance(value, (int, float))
3715
+ or not math.isfinite(value)
3716
+ or value < 0
3717
+ ):
3718
+ raise ValidationError(f"{label} must be finite and non-negative")
3719
+ return float(value)
3720
+
3721
+
3722
+ __all__ = [
3723
+ "LockedRetrieverSpec",
3724
+ "PreparedCorpusRetrieverBuilder",
3725
+ "ProductionBackendBuilder",
3726
+ "ProductionDownstreamPredictor",
3727
+ "ProductionPredictorBuilder",
3728
+ "ProductionRetrieverBuildRequest",
3729
+ "ProductionRetrieverBuilderProtocol",
3730
+ "ProductionRetrieverRouter",
3731
+ "builder_fingerprint_sha256",
3732
+ "prepared_corpus_retriever_builder_factory",
3733
+ "production_backend_builder_factory",
3734
+ "production_predictor_builder_factory",
3735
+ "retriever_product_sha256",
3736
+ "validate_predictor_product_sha256",
3737
+ "validate_retriever_product_sha256",
3738
+ "validate_runtime_product_sha256",
3739
+ ]