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,1129 @@
1
+ """License-gated external bridges for the two released RECOMP compressors.
2
+
3
+ The pinned repository exposes an extractive sentence scorer and a Hugging Face
4
+ seq2seq prediction script, rather than one online RECOMP runtime. These
5
+ adapters therefore keep model execution in a user-mounted worker and make the
6
+ small amount of AgentInputLab assembly explicit and auditable.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hashlib
12
+ import re
13
+ from collections.abc import Mapping
14
+ from dataclasses import asdict, dataclass, replace
15
+ from typing import Any
16
+
17
+ from agentinputlab.contracts import (
18
+ AlgorithmOutputError,
19
+ BudgetExceededError,
20
+ ContextBlock,
21
+ ExecutionTrace,
22
+ ExternalDependencyError,
23
+ FrozenAgentState,
24
+ InputPatch,
25
+ InterventionContext,
26
+ PatchOp,
27
+ PatchOperation,
28
+ SemanticFamily,
29
+ TraceEvent,
30
+ TracePhase,
31
+ ValidationError,
32
+ )
33
+ from agentinputlab.contracts.serialization import content_hash
34
+ from agentinputlab.integrations.external_workers import WorkerClient
35
+ from agentinputlab.integrations.external_workers.method import (
36
+ MethodWorkerRequest,
37
+ call_worker,
38
+ dependency_fingerprint,
39
+ finite_number,
40
+ mapping_sequence,
41
+ nonnegative_integer,
42
+ optional_text,
43
+ required_text,
44
+ resolve_worker,
45
+ string_tuple,
46
+ worker_fingerprint,
47
+ )
48
+ from agentinputlab.observability import external_usage_counters
49
+
50
+ RECOMP_UPSTREAM_COMMIT = "51d4432151efb3275257a9407dc71d1e5ec6634d"
51
+ RECOMP_EXTRACTIVE_PROTOCOL = "agentinputlab.recomp.extractive.v3"
52
+ RECOMP_ABSTRACTIVE_PROTOCOL = "agentinputlab.recomp.abstractive.v3"
53
+ RECOMP_CANDIDATE_UNIT_KIND = "presegmented_sentence"
54
+ RECOMP_COMMON_SUPPORT_RELEASE = "curate.recomp.common_support_extractive"
55
+ RECOMP_COMMON_SUPPORT_SPLITTER = "agentinputlab.visible_context_sentence_split.v1"
56
+ RECOMP_ABSTRACTIVE_INPUT_TEMPLATE = "Question: {question}\n Document: {passages}\n Summary: "
57
+ RECOMP_ABSTRACTIVE_INPUT_TEMPLATE_SHA256 = hashlib.sha256(
58
+ RECOMP_ABSTRACTIVE_INPUT_TEMPLATE.encode("utf-8")
59
+ ).hexdigest()
60
+ _HEX64 = re.compile(r"^[0-9a-f]{64}$")
61
+
62
+
63
+ @dataclass(frozen=True, slots=True)
64
+ class RECOMPExtractiveControls:
65
+ """AgentInputLab's explicit assembly policy for upstream sentence scores."""
66
+
67
+ max_sentences: int = 5
68
+ minimum_score: float | None = None
69
+ output_order: str = "source_order"
70
+ assembly_profile: str = "controlled"
71
+ dataset: str = ""
72
+
73
+ def __post_init__(self) -> None:
74
+ if (
75
+ isinstance(self.max_sentences, bool)
76
+ or not isinstance(self.max_sentences, int)
77
+ or (self.max_sentences != -1 and self.max_sentences < 1)
78
+ ):
79
+ raise ValidationError("RECOMP max_sentences must be -1 or positive")
80
+ if self.minimum_score is not None:
81
+ finite_number(self.minimum_score, label="RECOMP minimum_score")
82
+ if self.output_order not in {"source_order", "score_descending"}:
83
+ raise ValidationError("RECOMP output_order must be source_order or score_descending")
84
+ if self.assembly_profile not in {"controlled", "native_paper_qa"}:
85
+ raise ValidationError("RECOMP assembly_profile is unsupported")
86
+ if self.assembly_profile == "controlled":
87
+ if self.dataset:
88
+ raise ValidationError("controlled RECOMP assembly must not claim a paper dataset")
89
+ return
90
+ expected = {"nq": 1, "tqa": 1, "hotpotqa": 2}.get(self.dataset)
91
+ if expected is None:
92
+ raise ValidationError("paper-native RECOMP dataset must be nq, tqa, or hotpotqa")
93
+ if (
94
+ self.max_sentences != expected
95
+ or self.minimum_score is not None
96
+ or self.output_order != "score_descending"
97
+ ):
98
+ raise ValidationError(
99
+ "paper-native RECOMP requires top-1 NQ/TQA or top-2 HotpotQA by score"
100
+ )
101
+
102
+ @classmethod
103
+ def paper_native(cls, dataset: str) -> RECOMPExtractiveControls:
104
+ normalized = dataset.lower()
105
+ expected = {"nq": 1, "tqa": 1, "hotpotqa": 2}.get(normalized)
106
+ if expected is None:
107
+ raise ValidationError("paper-native RECOMP dataset must be nq, tqa, or hotpotqa")
108
+ return cls(
109
+ max_sentences=expected,
110
+ output_order="score_descending",
111
+ assembly_profile="native_paper_qa",
112
+ dataset=normalized,
113
+ )
114
+
115
+
116
+ @dataclass(frozen=True, slots=True)
117
+ class RECOMPAbstractiveControls:
118
+ max_source_tokens: int = 1024
119
+ max_target_tokens: int = 512
120
+ num_beams: int = 1
121
+ empty_output_policy: str = "clear_context"
122
+ input_assembly: str = "controlled_concatenate_no_separator"
123
+
124
+ def __post_init__(self) -> None:
125
+ if any(
126
+ isinstance(value, bool) or not isinstance(value, int) or value < 1
127
+ for value in (self.max_source_tokens, self.max_target_tokens, self.num_beams)
128
+ ):
129
+ raise ValidationError("RECOMP generation limits must be positive")
130
+ if self.empty_output_policy != "clear_context":
131
+ raise ValidationError(
132
+ "RECOMP empty output is selective augmentation and must clear context"
133
+ )
134
+ if self.input_assembly not in {
135
+ "controlled_concatenate_no_separator",
136
+ "prejoined_official_artifact",
137
+ }:
138
+ raise ValidationError("RECOMP abstractive input assembly is unsupported")
139
+
140
+
141
+ @dataclass(frozen=True, slots=True)
142
+ class RECOMPSentenceScore:
143
+ sentence_id: str
144
+ document_id: str
145
+ document_index: int
146
+ sentence_index: int
147
+ char_start: int
148
+ char_end: int
149
+ text: str
150
+ score: float
151
+
152
+ @property
153
+ def source_order(self) -> tuple[int, int, int]:
154
+ return (self.document_index, self.char_start, self.sentence_index)
155
+
156
+
157
+ @dataclass(frozen=True, slots=True)
158
+ class _SourceSentence:
159
+ block: ContextBlock
160
+ source_block_id: str
161
+ source_block_index: int
162
+ char_start: int
163
+ char_end: int
164
+
165
+
166
+ def _common_support_sentences(
167
+ context: tuple[ContextBlock, ...],
168
+ *,
169
+ max_candidates: int | None = None,
170
+ ) -> tuple[_SourceSentence, ...]:
171
+ """Split visible blocks into immutable RECOMP candidate units.
172
+
173
+ The upstream inference function consumes already segmented sentences. Its
174
+ repository does not publish the dataset-specific splitter, so the common-
175
+ support profile freezes a minimal punctuation/newline splitter and records
176
+ exact offsets back to the captured arena block. No hidden benchmark state
177
+ or answer label participates in this preprocessing step.
178
+ """
179
+
180
+ candidates: list[_SourceSentence] = []
181
+ boundary = re.compile(r"(?<=[.!?])\s+|\n+")
182
+ for block_index, source in enumerate(context):
183
+ source_id = source.block_id or f"context-{block_index}"
184
+ cursor = 0
185
+ sentence_index = 0
186
+ for match in (*boundary.finditer(source.text), None):
187
+ stop = len(source.text) if match is None else match.start()
188
+ raw = source.text[cursor:stop]
189
+ leading = len(raw) - len(raw.lstrip())
190
+ trailing = len(raw.rstrip())
191
+ start = cursor + leading
192
+ end = cursor + trailing
193
+ if end > start:
194
+ sentence_id = f"{source_id}:sentence:{sentence_index}"
195
+ candidates.append(
196
+ _SourceSentence(
197
+ block=ContextBlock(
198
+ block_id=sentence_id,
199
+ text=source.text[start:end],
200
+ source="recomp_common_support_preprocessor",
201
+ metadata={
202
+ "source_context_block_id": source_id,
203
+ "source_context_block_index": block_index,
204
+ "source_char_start": start,
205
+ "source_char_end": end,
206
+ "splitter_revision": RECOMP_COMMON_SUPPORT_SPLITTER,
207
+ },
208
+ ),
209
+ source_block_id=source_id,
210
+ source_block_index=block_index,
211
+ char_start=start,
212
+ char_end=end,
213
+ )
214
+ )
215
+ if max_candidates is not None and len(candidates) > max_candidates:
216
+ raise ValidationError("RECOMP sentence candidate limit exceeded; no truncation applied")
217
+ sentence_index += 1
218
+ if match is None:
219
+ break
220
+ cursor = match.end()
221
+ if not candidates:
222
+ raise ValidationError("RECOMP common-support requires non-empty visible context")
223
+ return tuple(candidates)
224
+
225
+
226
+ def prepare_recomp_sentence_state(
227
+ state: FrozenAgentState, *, max_candidates: int = 4096,
228
+ ) -> FrozenAgentState:
229
+ """Prepare bounded, source-offset-bound inputs for the extractive interface.
230
+
231
+ This is the same disclosed punctuation/newline splitter as the controlled
232
+ common-support adapter, not a claimed upstream dataset-specific splitter.
233
+ No model, retrieval, reference labels or network are used. Apply it to the
234
+ common input of both NO_OP and compressor arms, not just to the treatment.
235
+ """
236
+
237
+ if not isinstance(state, FrozenAgentState):
238
+ raise ValidationError("RECOMP sentence preparation requires a FrozenAgentState")
239
+ if type(max_candidates) is not int or max_candidates < 1:
240
+ raise ValidationError("RECOMP max_candidates must be a positive integer")
241
+ if "recomp_sentence_preparation" in state.metadata:
242
+ raise ValidationError("state already has RECOMP sentence preparation; use it directly")
243
+ sentences = _common_support_sentences(state.context, max_candidates=max_candidates)
244
+ ids = [item.block.block_id for item in sentences]
245
+ if len(ids) != len(set(ids)):
246
+ raise ValidationError("RECOMP sentence preparation has ambiguous source block IDs")
247
+ blocks = tuple(replace(item.block, metadata={
248
+ **dict(item.block.metadata), "source_captured_state_hash": state.state_hash,
249
+ }) for item in sentences)
250
+ return replace(state, context=blocks, metadata={
251
+ **dict(state.metadata),
252
+ "recomp_sentence_preparation": {
253
+ "splitter_revision": RECOMP_COMMON_SUPPORT_SPLITTER,
254
+ "source_state_hash": state.state_hash,
255
+ "source_context_blocks": len(state.context),
256
+ "candidate_sentences": len(blocks),
257
+ "max_candidates": max_candidates,
258
+ "reference_or_network_access": False,
259
+ "paper_native_preprocessing_claimed": False,
260
+ },
261
+ })
262
+
263
+
264
+ @dataclass(slots=True)
265
+ class RECOMPExtractiveCommonSupport:
266
+ """Source-faithful scorer plus a frozen visible-context sentence adapter."""
267
+
268
+ checkpoint_revision: str = ""
269
+ tokenizer_revision: str = ""
270
+ asset_manifest_hash: str = ""
271
+ asset_license_acknowledged: bool = False
272
+ controls: RECOMPExtractiveControls = RECOMPExtractiveControls()
273
+ method_id: str = "recomp"
274
+ release_id: str = RECOMP_COMMON_SUPPORT_RELEASE
275
+ family: SemanticFamily = SemanticFamily.CURATE
276
+
277
+ def __post_init__(self) -> None:
278
+ _validate_asset_identity(
279
+ checkpoint_revision=self.checkpoint_revision,
280
+ tokenizer_revision=self.tokenizer_revision,
281
+ asset_manifest_hash=self.asset_manifest_hash,
282
+ )
283
+
284
+ def propose(self, state: FrozenAgentState, context: InterventionContext) -> InputPatch:
285
+ sentences = _common_support_sentences(state.context)
286
+ derived = replace(state, context=tuple(item.block for item in sentences))
287
+ component = RECOMPExtractiveExternal(
288
+ checkpoint_revision=self.checkpoint_revision,
289
+ tokenizer_revision=self.tokenizer_revision,
290
+ sentence_splitter_revision=RECOMP_COMMON_SUPPORT_SPLITTER,
291
+ asset_manifest_hash=self.asset_manifest_hash,
292
+ asset_license_acknowledged=self.asset_license_acknowledged,
293
+ controls=self.controls,
294
+ ).propose(derived, context)
295
+ source_by_sentence = {item.block.block_id: item for item in sentences}
296
+ operations: list[PatchOperation] = []
297
+ for operation in component.operations:
298
+ if operation.op is not PatchOp.REPLACE_CONTEXT:
299
+ raise AlgorithmOutputError(
300
+ "RECOMP common-support component emitted an unexpected patch operation"
301
+ )
302
+ selected: list[ContextBlock] = []
303
+ for block in operation.value:
304
+ sentence_id = str(block.metadata.get("source_document_id", ""))
305
+ source = source_by_sentence.get(sentence_id)
306
+ if source is None or block.text != source.block.text:
307
+ raise AlgorithmOutputError(
308
+ "RECOMP common-support output lost its source sentence binding"
309
+ )
310
+ selected.append(
311
+ ContextBlock(
312
+ block_id=block.block_id,
313
+ text=block.text,
314
+ source=block.source,
315
+ score=block.score,
316
+ metadata={
317
+ **dict(block.metadata),
318
+ "source_context_block_id": source.source_block_id,
319
+ "source_context_block_index": source.source_block_index,
320
+ "source_char_start": source.char_start,
321
+ "source_char_end": source.char_end,
322
+ "source_captured_state_hash": state.state_hash,
323
+ "splitter_revision": RECOMP_COMMON_SUPPORT_SPLITTER,
324
+ },
325
+ )
326
+ )
327
+ operations.append(
328
+ PatchOperation(
329
+ PatchOp.REPLACE_CONTEXT,
330
+ tuple(selected),
331
+ target=operation.target,
332
+ rationale=(
333
+ "official RECOMP sentence scores after frozen visible-context "
334
+ "presegmentation"
335
+ ),
336
+ )
337
+ )
338
+ trace = ExecutionTrace(
339
+ method_id=self.method_id,
340
+ release_id=self.release_id,
341
+ events=(
342
+ TraceEvent(
343
+ TracePhase.PREPARE,
344
+ "recomp_common_support_presegmentation",
345
+ {
346
+ "splitter_revision": RECOMP_COMMON_SUPPORT_SPLITTER,
347
+ "source_state_hash": state.state_hash,
348
+ "derived_state_hash": derived.state_hash,
349
+ "source_block_count": len(state.context),
350
+ "candidate_sentence_count": len(sentences),
351
+ "candidate_manifest_hash": content_hash(
352
+ tuple(
353
+ {
354
+ "sentence_id": item.block.block_id,
355
+ "text": item.block.text,
356
+ "source_block_id": item.source_block_id,
357
+ "source_block_index": item.source_block_index,
358
+ "char_start": item.char_start,
359
+ "char_end": item.char_end,
360
+ }
361
+ for item in sentences
362
+ )
363
+ ),
364
+ },
365
+ ),
366
+ *component.trace.events,
367
+ ),
368
+ counters={
369
+ **dict(component.trace.counters),
370
+ "source_context_blocks": len(state.context),
371
+ "candidate_sentences": len(sentences),
372
+ },
373
+ warnings=(
374
+ *component.trace.warnings,
375
+ "controlled_common_support_sentence_splitter_not_paper_result_parity",
376
+ ),
377
+ )
378
+ return InputPatch(
379
+ method_id=self.method_id,
380
+ release_id=self.release_id,
381
+ source_state_hash=state.state_hash,
382
+ operations=tuple(operations),
383
+ trace=trace,
384
+ implementation_identity=(
385
+ "common_support_preprocessor+"
386
+ f"thin_external_wrapper:carriex/recomp@{RECOMP_UPSTREAM_COMMIT}"
387
+ ),
388
+ profile_kind="controlled_adapter",
389
+ metadata={
390
+ **dict(component.metadata),
391
+ "component_release_id": component.release_id,
392
+ "common_support_splitter_revision": RECOMP_COMMON_SUPPORT_SPLITTER,
393
+ "source_state_hash": state.state_hash,
394
+ "derived_candidate_state_hash": derived.state_hash,
395
+ "paper_result_parity_claimed": False,
396
+ },
397
+ )
398
+
399
+
400
+ @dataclass(slots=True)
401
+ class RECOMPExtractiveExternal:
402
+ """Run the official scorer externally, then apply a frozen assembly rule."""
403
+
404
+ client: WorkerClient | None = None
405
+ checkpoint_revision: str = ""
406
+ tokenizer_revision: str = ""
407
+ sentence_splitter_revision: str = ""
408
+ asset_manifest_hash: str = ""
409
+ asset_license_acknowledged: bool = False
410
+ controls: RECOMPExtractiveControls = RECOMPExtractiveControls()
411
+ method_id: str = "recomp"
412
+ release_id: str = "curate.recomp.extractive"
413
+ family: SemanticFamily = SemanticFamily.CURATE
414
+
415
+ def __post_init__(self) -> None:
416
+ _validate_asset_identity(
417
+ checkpoint_revision=self.checkpoint_revision,
418
+ tokenizer_revision=self.tokenizer_revision,
419
+ asset_manifest_hash=self.asset_manifest_hash,
420
+ )
421
+ if not self.sentence_splitter_revision.strip():
422
+ raise ValidationError("RECOMP sentence_splitter_revision must not be empty")
423
+
424
+ def propose(self, state: FrozenAgentState, context: InterventionContext) -> InputPatch:
425
+ document_ids = _document_ids(state)
426
+ _require_asset_acknowledgement(self.asset_license_acknowledged)
427
+ worker = resolve_worker(self.client, context, label="RECOMP extractive")
428
+ worker_identity = worker_fingerprint(worker, label="RECOMP extractive")
429
+ retriever_fp = dependency_fingerprint(
430
+ context.retriever, label="RECOMP upstream retriever", required=True
431
+ )
432
+ retriever_hash = content_hash(retriever_fp)
433
+ input_hash = content_hash(
434
+ {
435
+ "query": state.query,
436
+ "documents": state.context,
437
+ "document_ids": document_ids,
438
+ "candidate_unit_kind": RECOMP_CANDIDATE_UNIT_KIND,
439
+ }
440
+ )
441
+ remaining_budget = _remaining_execution_budget(state, context)
442
+ _require_local_compute_budget(
443
+ remaining_budget,
444
+ output_tokens_required=0,
445
+ )
446
+ base_payload = {
447
+ "task_id": state.task_id,
448
+ "query": state.query,
449
+ "documents": state.context,
450
+ "document_ids": document_ids,
451
+ "checkpoint_revision": self.checkpoint_revision,
452
+ "tokenizer_revision": self.tokenizer_revision,
453
+ "sentence_splitter_revision": self.sentence_splitter_revision,
454
+ "asset_manifest_hash": self.asset_manifest_hash,
455
+ "retriever_artifact_hash": retriever_hash,
456
+ "input_artifact_hash": input_hash,
457
+ "candidate_unit_kind": RECOMP_CANDIDATE_UNIT_KIND,
458
+ "controls": asdict(self.controls),
459
+ "seed": context.seed,
460
+ }
461
+ preflight_payload, response = _preflight_then_execute(
462
+ worker=worker,
463
+ worker_identity=worker_identity,
464
+ method_id=self.method_id,
465
+ release_id=self.release_id,
466
+ protocol=RECOMP_EXTRACTIVE_PROTOCOL,
467
+ state_hash=state.state_hash,
468
+ base_payload=base_payload,
469
+ dependencies={"retriever": retriever_fp},
470
+ remaining_budget=remaining_budget,
471
+ )
472
+ _validate_response_identity(
473
+ response.payload,
474
+ checkpoint_revision=self.checkpoint_revision,
475
+ tokenizer_revision=self.tokenizer_revision,
476
+ asset_manifest_hash=self.asset_manifest_hash,
477
+ document_ids=document_ids,
478
+ retriever_artifact_hash=retriever_hash,
479
+ input_artifact_hash=input_hash,
480
+ )
481
+ _validate_worker_attestation(response.payload, worker_identity)
482
+ if response.payload.get("candidate_unit_kind") != RECOMP_CANDIDATE_UNIT_KIND:
483
+ raise AlgorithmOutputError("RECOMP worker changed fixed candidate units")
484
+ if response.payload.get("scoring_api") != (
485
+ "run_extractive_compressor.get_contriever_scores(top_k=-1)"
486
+ ):
487
+ raise AlgorithmOutputError("RECOMP worker did not use the pinned scoring API")
488
+ if response.usage.model_calls != 1 or response.usage.retrieval_calls != 0:
489
+ raise AlgorithmOutputError("RECOMP scorer usage is inconsistent")
490
+ returned_splitter = required_text(
491
+ response.payload,
492
+ "sentence_splitter_revision",
493
+ label="RECOMP extractive response",
494
+ )
495
+ if returned_splitter != self.sentence_splitter_revision:
496
+ raise AlgorithmOutputError("RECOMP sentence splitter revision mismatch")
497
+ scores = _parse_sentence_scores(response.payload.get("sentence_scores"), state)
498
+ selected = _select_sentences(scores, self.controls)
499
+ blocks = tuple(
500
+ ContextBlock(
501
+ block_id=f"recomp:{record.sentence_id}",
502
+ text=record.text,
503
+ source="recomp_extractive",
504
+ score=record.score,
505
+ metadata={
506
+ "source_document_id": record.document_id,
507
+ "source_document_index": record.document_index,
508
+ "source_sentence_index": record.sentence_index,
509
+ "source_char_start": record.char_start,
510
+ "source_char_end": record.char_end,
511
+ "source_state_hash": state.state_hash,
512
+ },
513
+ )
514
+ for record in selected
515
+ )
516
+ trace = ExecutionTrace(
517
+ method_id=self.method_id,
518
+ release_id=self.release_id,
519
+ events=(
520
+ TraceEvent(
521
+ TracePhase.PREPARE,
522
+ "recomp_extractive_assets",
523
+ {
524
+ "checkpoint_revision": self.checkpoint_revision,
525
+ "tokenizer_revision": self.tokenizer_revision,
526
+ "sentence_splitter_revision": self.sentence_splitter_revision,
527
+ "document_ids": document_ids,
528
+ },
529
+ ),
530
+ TraceEvent(
531
+ TracePhase.OBSERVE,
532
+ "official_sentence_scores",
533
+ {
534
+ "sentence_ids": tuple(item.sentence_id for item in scores),
535
+ "scores": tuple(item.score for item in scores),
536
+ },
537
+ ),
538
+ TraceEvent(
539
+ TracePhase.FINALIZE,
540
+ "agentinputlab_assembly",
541
+ {
542
+ "controls": asdict(self.controls),
543
+ "selected_sentence_ids": tuple(item.sentence_id for item in selected),
544
+ },
545
+ ),
546
+ ),
547
+ counters={
548
+ **external_usage_counters(response.usage),
549
+ "worker_calls": 2,
550
+ "scored_sentences": len(scores),
551
+ "selected_sentences": len(selected),
552
+ },
553
+ )
554
+ return InputPatch(
555
+ method_id=self.method_id,
556
+ release_id=self.release_id,
557
+ source_state_hash=state.state_hash,
558
+ operations=(
559
+ PatchOperation(
560
+ PatchOp.REPLACE_CONTEXT,
561
+ blocks,
562
+ target="compressed_retrieved_context",
563
+ rationale="official RECOMP scores with a frozen AgentInputLab assembly policy",
564
+ ),
565
+ ),
566
+ trace=trace,
567
+ implementation_identity=(
568
+ f"thin_external_wrapper:carriex/recomp@{RECOMP_UPSTREAM_COMMIT}"
569
+ ),
570
+ profile_kind=(
571
+ "paper_native_adapter"
572
+ if self.controls.assembly_profile == "native_paper_qa"
573
+ else "controlled_adapter"
574
+ ),
575
+ metadata={
576
+ "implementation_origin": "official_scorer_plus_controlled_assembly",
577
+ "known_deviation": "upstream repository does not provide online assembly",
578
+ "asset_license": "NOASSERTION_acknowledged_by_user",
579
+ "asset_manifest_hash": self.asset_manifest_hash,
580
+ "assembly_policy_hash": content_hash(asdict(self.controls)),
581
+ "assembly_profile": self.controls.assembly_profile,
582
+ "retriever_artifact_hash": retriever_hash,
583
+ "input_artifact_hash": input_hash,
584
+ "compatibility_manifest_sha256": response.payload["compatibility_manifest_sha256"],
585
+ "execution_evidence": response.payload["execution_evidence"],
586
+ "worker_fingerprint": worker_identity,
587
+ "runtime_fingerprint": response.payload["runtime_fingerprint"],
588
+ "runtime_fingerprint_hash": response.payload["runtime_fingerprint_hash"],
589
+ "checkpoint_tree_sha256": response.payload["checkpoint_tree_sha256"],
590
+ "preflight_token": preflight_payload["preflight_token"],
591
+ },
592
+ )
593
+
594
+
595
+ @dataclass(slots=True)
596
+ class RECOMPAbstractiveExternal:
597
+ """Bridge the official seq2seq prediction path, preserving empty summaries."""
598
+
599
+ client: WorkerClient | None = None
600
+ checkpoint_revision: str = ""
601
+ tokenizer_revision: str = ""
602
+ input_template_hash: str = ""
603
+ asset_manifest_hash: str = ""
604
+ asset_license_acknowledged: bool = False
605
+ controls: RECOMPAbstractiveControls = RECOMPAbstractiveControls()
606
+ method_id: str = "recomp"
607
+ release_id: str = "curate.recomp.abstractive"
608
+ family: SemanticFamily = SemanticFamily.CURATE
609
+
610
+ def __post_init__(self) -> None:
611
+ _validate_asset_identity(
612
+ checkpoint_revision=self.checkpoint_revision,
613
+ tokenizer_revision=self.tokenizer_revision,
614
+ asset_manifest_hash=self.asset_manifest_hash,
615
+ )
616
+ if self.input_template_hash != RECOMP_ABSTRACTIVE_INPUT_TEMPLATE_SHA256:
617
+ raise ValidationError(
618
+ "RECOMP input_template_hash must match the pinned official prompt"
619
+ )
620
+
621
+ def propose(self, state: FrozenAgentState, context: InterventionContext) -> InputPatch:
622
+ document_ids = _document_ids(state)
623
+ _require_asset_acknowledgement(self.asset_license_acknowledged)
624
+ worker = resolve_worker(self.client, context, label="RECOMP abstractive")
625
+ worker_identity = worker_fingerprint(worker, label="RECOMP abstractive")
626
+ retriever_fp = dependency_fingerprint(
627
+ context.retriever, label="RECOMP upstream retriever", required=True
628
+ )
629
+ retriever_hash = content_hash(retriever_fp)
630
+ if self.controls.input_assembly == "prejoined_official_artifact":
631
+ if len(state.context) != 1:
632
+ raise ValidationError("prejoined RECOMP input requires exactly one context block")
633
+ passages = state.context[0].text
634
+ else:
635
+ passages = "".join(block.text for block in state.context)
636
+ passages_hash = content_hash(passages)
637
+ input_hash = content_hash(
638
+ {
639
+ "query": state.query,
640
+ "documents": state.context,
641
+ "document_ids": document_ids,
642
+ "passages": passages,
643
+ "input_template_hash": self.input_template_hash,
644
+ }
645
+ )
646
+ remaining_budget = _remaining_execution_budget(state, context)
647
+ _require_local_compute_budget(
648
+ remaining_budget,
649
+ output_tokens_required=self.controls.max_target_tokens,
650
+ )
651
+ base_payload = {
652
+ "task_id": state.task_id,
653
+ "query": state.query,
654
+ "documents": state.context,
655
+ "document_ids": document_ids,
656
+ "passages": passages,
657
+ "passages_hash": passages_hash,
658
+ "checkpoint_revision": self.checkpoint_revision,
659
+ "tokenizer_revision": self.tokenizer_revision,
660
+ "input_template_hash": self.input_template_hash,
661
+ "asset_manifest_hash": self.asset_manifest_hash,
662
+ "retriever_artifact_hash": retriever_hash,
663
+ "input_artifact_hash": input_hash,
664
+ "controls": asdict(self.controls),
665
+ "seed": context.seed,
666
+ }
667
+ preflight_payload, response = _preflight_then_execute(
668
+ worker=worker,
669
+ worker_identity=worker_identity,
670
+ method_id=self.method_id,
671
+ release_id=self.release_id,
672
+ protocol=RECOMP_ABSTRACTIVE_PROTOCOL,
673
+ state_hash=state.state_hash,
674
+ base_payload=base_payload,
675
+ dependencies={"retriever": retriever_fp},
676
+ remaining_budget=remaining_budget,
677
+ )
678
+ payload = response.payload
679
+ _validate_response_identity(
680
+ payload,
681
+ checkpoint_revision=self.checkpoint_revision,
682
+ tokenizer_revision=self.tokenizer_revision,
683
+ asset_manifest_hash=self.asset_manifest_hash,
684
+ document_ids=document_ids,
685
+ retriever_artifact_hash=retriever_hash,
686
+ input_artifact_hash=input_hash,
687
+ )
688
+ _validate_worker_attestation(payload, worker_identity)
689
+ if (
690
+ required_text(payload, "input_template_hash", label="RECOMP abstractive response")
691
+ != self.input_template_hash
692
+ ):
693
+ raise AlgorithmOutputError("RECOMP input template hash mismatch")
694
+ if payload.get("passages_hash") != passages_hash:
695
+ raise AlgorithmOutputError("RECOMP passages hash mismatch")
696
+ expected_prompt = RECOMP_ABSTRACTIVE_INPUT_TEMPLATE.format(
697
+ question=state.query, passages=passages
698
+ )
699
+ if payload.get("prompt_hash") != hashlib.sha256(expected_prompt.encode()).hexdigest():
700
+ raise AlgorithmOutputError("RECOMP official prompt hash mismatch")
701
+ if payload.get("generation_api") != ("AutoModelForSeq2SeqLM.generate+batch_decode(strip)"):
702
+ raise AlgorithmOutputError("RECOMP worker did not use the pinned generation path")
703
+ if payload.get("applied") is not True:
704
+ raise AlgorithmOutputError(
705
+ "successful RECOMP generation must report applied=true, including empty output"
706
+ )
707
+ summary = optional_text(payload, "summary", label="RECOMP abstractive response")
708
+ raw_generation = optional_text(
709
+ payload, "raw_generation", label="RECOMP abstractive response"
710
+ )
711
+ if summary != summary.strip():
712
+ raise AlgorithmOutputError("RECOMP summary must be normalized without edge whitespace")
713
+ selective_empty = not summary
714
+ if payload.get("selective_augmentation") is not selective_empty:
715
+ raise AlgorithmOutputError("RECOMP selective-augmentation flag disagrees with output")
716
+ parse_status = required_text(payload, "parse_status", label="RECOMP abstractive response")
717
+ expected_status = "generated_empty" if selective_empty else "generated_text"
718
+ if parse_status != expected_status:
719
+ raise AlgorithmOutputError("RECOMP generation parse status is inconsistent")
720
+ if response.usage.model_calls < 1:
721
+ raise AlgorithmOutputError("RECOMP abstractive generation must report a model call")
722
+
723
+ blocks: tuple[ContextBlock, ...]
724
+ if selective_empty:
725
+ blocks = ()
726
+ else:
727
+ blocks = (
728
+ ContextBlock(
729
+ block_id=f"{self.release_id}:summary",
730
+ text=summary,
731
+ source="recomp_abstractive",
732
+ metadata={
733
+ "source_document_ids": document_ids,
734
+ "source_state_hash": state.state_hash,
735
+ "raw_generation_hash": content_hash(raw_generation),
736
+ },
737
+ ),
738
+ )
739
+ trace = ExecutionTrace(
740
+ method_id=self.method_id,
741
+ release_id=self.release_id,
742
+ events=(
743
+ TraceEvent(
744
+ TracePhase.PREPARE,
745
+ "recomp_abstractive_assets",
746
+ {
747
+ "checkpoint_revision": self.checkpoint_revision,
748
+ "tokenizer_revision": self.tokenizer_revision,
749
+ "input_template_hash": self.input_template_hash,
750
+ "document_ids": document_ids,
751
+ },
752
+ ),
753
+ TraceEvent(
754
+ TracePhase.FINALIZE,
755
+ "selective_generation",
756
+ {
757
+ "parse_status": parse_status,
758
+ "selective_augmentation": selective_empty,
759
+ "raw_generation": raw_generation,
760
+ "selected_context_count": len(blocks),
761
+ },
762
+ ),
763
+ ),
764
+ counters={
765
+ **external_usage_counters(response.usage),
766
+ "worker_calls": 2,
767
+ "selected_context_blocks": len(blocks),
768
+ },
769
+ warnings=("selective_augmentation_cleared_context",) if selective_empty else (),
770
+ )
771
+ return InputPatch(
772
+ method_id=self.method_id,
773
+ release_id=self.release_id,
774
+ source_state_hash=state.state_hash,
775
+ operations=(
776
+ PatchOperation(
777
+ PatchOp.REPLACE_CONTEXT,
778
+ blocks,
779
+ target="compressed_retrieved_context",
780
+ rationale=(
781
+ "successful empty RECOMP output disables augmentation"
782
+ if selective_empty
783
+ else "official RECOMP abstractive compressor output"
784
+ ),
785
+ ),
786
+ ),
787
+ trace=trace,
788
+ implementation_identity=(
789
+ f"thin_external_wrapper:carriex/recomp@{RECOMP_UPSTREAM_COMMIT}"
790
+ ),
791
+ profile_kind=(
792
+ "official_prompt_adapter"
793
+ if self.controls.input_assembly == "prejoined_official_artifact"
794
+ else "controlled_adapter"
795
+ ),
796
+ metadata={
797
+ "implementation_origin": "thin_external_wrapper",
798
+ "asset_license": "NOASSERTION_acknowledged_by_user",
799
+ "asset_manifest_hash": self.asset_manifest_hash,
800
+ "selective_augmentation": selective_empty,
801
+ "applied": True,
802
+ "raw_generation": raw_generation,
803
+ "input_assembly": self.controls.input_assembly,
804
+ "retriever_artifact_hash": retriever_hash,
805
+ "input_artifact_hash": input_hash,
806
+ "compatibility_manifest_sha256": payload["compatibility_manifest_sha256"],
807
+ "execution_evidence": payload["execution_evidence"],
808
+ "worker_fingerprint": worker_identity,
809
+ "runtime_fingerprint": payload["runtime_fingerprint"],
810
+ "runtime_fingerprint_hash": payload["runtime_fingerprint_hash"],
811
+ "checkpoint_tree_sha256": payload["checkpoint_tree_sha256"],
812
+ "preflight_token": preflight_payload["preflight_token"],
813
+ },
814
+ )
815
+
816
+
817
+ def _remaining_execution_budget(
818
+ state: FrozenAgentState, context: InterventionContext
819
+ ) -> Mapping[str, int | float | None]:
820
+ usage = None
821
+ ledger = context.budget_ledger
822
+ if ledger is not None:
823
+ ledger_budget = getattr(ledger, "budget", None)
824
+ usage = getattr(ledger, "usage", None)
825
+ if ledger_budget != state.budget or usage is None:
826
+ raise ValidationError("RECOMP received an incompatible budget ledger")
827
+
828
+ def consumed_integer(name: str) -> int:
829
+ value = 0 if usage is None else getattr(usage, name, None)
830
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
831
+ raise ValidationError(f"RECOMP budget usage {name} is invalid")
832
+ return value
833
+
834
+ remaining_context = None
835
+ if state.budget.max_context_tokens is not None:
836
+ remaining_context = max(
837
+ 0,
838
+ state.budget.max_context_tokens - consumed_integer("input_tokens"),
839
+ )
840
+ remaining_cost: float | None = None
841
+ if state.budget.max_cost_usd is not None:
842
+ consumed_cost = 0.0 if usage is None else getattr(usage, "total_cost_usd", None)
843
+ if (
844
+ isinstance(consumed_cost, bool)
845
+ or not isinstance(consumed_cost, (int, float))
846
+ or consumed_cost < 0
847
+ ):
848
+ raise ValidationError("RECOMP budget usage total_cost_usd is invalid")
849
+ remaining_cost = max(0.0, float(state.budget.max_cost_usd) - float(consumed_cost))
850
+ return {
851
+ "model_calls": max(0, state.budget.max_model_calls - consumed_integer("model_calls")),
852
+ "context_tokens": remaining_context,
853
+ "output_tokens": max(0, state.budget.max_output_tokens - consumed_integer("output_tokens")),
854
+ "cost_usd": remaining_cost,
855
+ }
856
+
857
+
858
+ def _require_local_compute_budget(
859
+ budget: Mapping[str, int | float | None], *, output_tokens_required: int
860
+ ) -> None:
861
+ if budget["model_calls"] < 1: # type: ignore[operator]
862
+ raise BudgetExceededError("RECOMP requires one remaining model call")
863
+ if budget["output_tokens"] < output_tokens_required: # type: ignore[operator]
864
+ raise BudgetExceededError(
865
+ "RECOMP remaining output-token budget is below the generation maximum"
866
+ )
867
+ if budget["cost_usd"] is not None:
868
+ raise BudgetExceededError(
869
+ "RECOMP local inference is unpriced; a configured cost cap cannot be guaranteed"
870
+ )
871
+
872
+
873
+ def _preflight_then_execute(
874
+ *,
875
+ worker: WorkerClient,
876
+ worker_identity: Mapping[str, Any],
877
+ method_id: str,
878
+ release_id: str,
879
+ protocol: str,
880
+ state_hash: str,
881
+ base_payload: Mapping[str, Any],
882
+ dependencies: Mapping[str, Any],
883
+ remaining_budget: Mapping[str, int | float | None],
884
+ ) -> tuple[Mapping[str, Any], Any]:
885
+ def request(*, phase: str, token: str) -> MethodWorkerRequest:
886
+ return MethodWorkerRequest(
887
+ method_id=method_id,
888
+ release_id=release_id,
889
+ upstream_commit=RECOMP_UPSTREAM_COMMIT,
890
+ state_hash=state_hash,
891
+ payload={
892
+ **base_payload,
893
+ "phase": phase,
894
+ "preflight_token": token,
895
+ "checkpoint_license_acknowledged": True,
896
+ "remaining_budget": remaining_budget,
897
+ },
898
+ dependency_fingerprints=dependencies,
899
+ minimum_reservation={} if phase == "preflight" else {"model_calls": 1},
900
+ protocol_version=protocol,
901
+ )
902
+
903
+ preflight = call_worker(worker, request(phase="preflight", token=""))
904
+ if (
905
+ preflight.usage.model_calls
906
+ or preflight.usage.retrieval_calls
907
+ or preflight.usage.input_tokens
908
+ or preflight.usage.output_tokens
909
+ or preflight.usage.unpriced_model_calls
910
+ ):
911
+ raise AlgorithmOutputError("RECOMP preflight must report zero inference usage")
912
+ payload = preflight.payload
913
+ _validate_worker_attestation(payload, worker_identity)
914
+ _validate_preflight_payload(payload, remaining_budget=remaining_budget)
915
+ token = required_text(payload, "preflight_token", label="RECOMP preflight")
916
+ execute = call_worker(worker, request(phase="execute", token=token))
917
+ _validate_worker_attestation(execute.payload, worker_identity)
918
+ _validate_preflight_payload(execute.payload, remaining_budget=remaining_budget)
919
+ for key in (
920
+ "preflight_token",
921
+ "runtime_fingerprint_hash",
922
+ "checkpoint_tree_sha256",
923
+ "remaining_budget_hash",
924
+ "input_tokens_required",
925
+ "output_tokens_required",
926
+ ):
927
+ if execute.payload.get(key) != payload.get(key):
928
+ raise AlgorithmOutputError(f"RECOMP {key} changed after preflight")
929
+ return payload, execute
930
+
931
+
932
+ def _validate_preflight_payload(
933
+ payload: Mapping[str, Any],
934
+ *,
935
+ remaining_budget: Mapping[str, int | float | None],
936
+ ) -> None:
937
+ if (
938
+ payload.get("checkpoint_license") != "NOASSERTION"
939
+ or payload.get("checkpoint_license_acknowledged") is not True
940
+ ):
941
+ raise AlgorithmOutputError("RECOMP checkpoint license acknowledgement was not attested")
942
+ if payload.get("remaining_budget_hash") != content_hash(remaining_budget):
943
+ raise AlgorithmOutputError("RECOMP worker budget binding mismatch")
944
+ input_required = nonnegative_integer(
945
+ payload.get("input_tokens_required"), label="RECOMP input_tokens_required"
946
+ )
947
+ output_required = nonnegative_integer(
948
+ payload.get("output_tokens_required"), label="RECOMP output_tokens_required"
949
+ )
950
+ context_cap = remaining_budget["context_tokens"]
951
+ if context_cap is not None and input_required > context_cap:
952
+ raise AlgorithmOutputError("RECOMP preflight exceeded the sent context-token budget")
953
+ if output_required > remaining_budget["output_tokens"]: # type: ignore[operator]
954
+ raise AlgorithmOutputError("RECOMP preflight exceeded the sent output-token budget")
955
+ runtime = payload.get("runtime_fingerprint")
956
+ if not isinstance(runtime, Mapping) or not runtime:
957
+ raise AlgorithmOutputError("RECOMP response omits runtime fingerprint")
958
+ runtime_hash = required_text(payload, "runtime_fingerprint_hash", label="RECOMP response")
959
+ if runtime_hash != content_hash(runtime):
960
+ raise AlgorithmOutputError("RECOMP runtime fingerprint hash mismatch")
961
+ required_keys = {
962
+ "device",
963
+ "python_version",
964
+ "python_implementation",
965
+ "dependency_versions",
966
+ "container_digest",
967
+ "custom_model_loader",
968
+ }
969
+ if set(runtime) != required_keys or not isinstance(runtime.get("dependency_versions"), Mapping):
970
+ raise AlgorithmOutputError("RECOMP runtime fingerprint schema mismatch")
971
+ for key in ("checkpoint_tree_sha256", "preflight_token"):
972
+ if not _HEX64.fullmatch(required_text(payload, key, label="RECOMP response")):
973
+ raise AlgorithmOutputError(f"RECOMP {key} must be a SHA-256 digest")
974
+ nonnegative_integer(payload.get("checkpoint_file_count"), label="RECOMP checkpoint_file_count")
975
+
976
+
977
+ def _validate_asset_identity(
978
+ *, checkpoint_revision: str, tokenizer_revision: str, asset_manifest_hash: str
979
+ ) -> None:
980
+ for label, value in (
981
+ ("checkpoint_revision", checkpoint_revision),
982
+ ("tokenizer_revision", tokenizer_revision),
983
+ ("asset_manifest_hash", asset_manifest_hash),
984
+ ):
985
+ if not isinstance(value, str) or not value.strip():
986
+ raise ValidationError(f"RECOMP {label} must not be empty")
987
+ if not _HEX64.fullmatch(asset_manifest_hash):
988
+ raise ValidationError("RECOMP asset_manifest_hash must be a SHA-256 digest")
989
+
990
+
991
+ def _require_asset_acknowledgement(acknowledged: bool) -> None:
992
+ if not acknowledged:
993
+ raise ExternalDependencyError(
994
+ "RECOMP model artifacts are NOASSERTION; explicitly acknowledge the "
995
+ "user-mounted asset license before execution"
996
+ )
997
+
998
+
999
+ def _validate_worker_attestation(
1000
+ payload: Mapping[str, Any], worker_identity: Mapping[str, Any]
1001
+ ) -> None:
1002
+ for key in ("compatibility_manifest_sha256", "execution_evidence"):
1003
+ expected = worker_identity.get(key)
1004
+ if not isinstance(expected, str) or not expected:
1005
+ raise ValidationError(f"RECOMP worker fingerprint omits {key}")
1006
+ if payload.get(key) != expected:
1007
+ raise AlgorithmOutputError(f"RECOMP worker {key} attestation mismatch")
1008
+ expected_runtime = worker_identity.get("runtime_fingerprint_hash")
1009
+ if expected_runtime is not None:
1010
+ if not isinstance(expected_runtime, str) or not _HEX64.fullmatch(expected_runtime):
1011
+ raise ValidationError("RECOMP worker fingerprint runtime hash is invalid")
1012
+ if payload.get("runtime_fingerprint_hash") != expected_runtime:
1013
+ raise AlgorithmOutputError("RECOMP worker runtime fingerprint attestation mismatch")
1014
+
1015
+
1016
+ def _document_ids(state: FrozenAgentState) -> tuple[str, ...]:
1017
+ if not state.context:
1018
+ raise ValidationError("RECOMP requires already-retrieved context")
1019
+ values = tuple(block.block_id for block in state.context)
1020
+ if any(not value for value in values) or len(values) != len(set(values)):
1021
+ raise ValidationError("RECOMP documents require unique non-empty block IDs")
1022
+ return values
1023
+
1024
+
1025
+ def _validate_response_identity(
1026
+ payload: Mapping[str, Any],
1027
+ *,
1028
+ checkpoint_revision: str,
1029
+ tokenizer_revision: str,
1030
+ asset_manifest_hash: str,
1031
+ document_ids: tuple[str, ...],
1032
+ retriever_artifact_hash: str,
1033
+ input_artifact_hash: str,
1034
+ ) -> None:
1035
+ for key, expected in (
1036
+ ("checkpoint_revision", checkpoint_revision),
1037
+ ("tokenizer_revision", tokenizer_revision),
1038
+ ("asset_manifest_hash", asset_manifest_hash),
1039
+ ):
1040
+ if required_text(payload, key, label="RECOMP response") != expected:
1041
+ raise AlgorithmOutputError(f"RECOMP {key} mismatch")
1042
+ returned_ids = string_tuple(
1043
+ payload.get("source_document_ids"),
1044
+ label="RECOMP source_document_ids",
1045
+ allow_empty=False,
1046
+ unique=True,
1047
+ )
1048
+ if returned_ids != document_ids:
1049
+ raise AlgorithmOutputError("RECOMP worker changed source document order")
1050
+ for key, expected in (
1051
+ ("retriever_artifact_hash", retriever_artifact_hash),
1052
+ ("input_artifact_hash", input_artifact_hash),
1053
+ ):
1054
+ if required_text(payload, key, label="RECOMP response") != expected:
1055
+ raise AlgorithmOutputError(f"RECOMP {key} mismatch")
1056
+ for key in ("compatibility_manifest_sha256",):
1057
+ if not _HEX64.fullmatch(required_text(payload, key, label="RECOMP response")):
1058
+ raise AlgorithmOutputError(f"RECOMP {key} must be a SHA-256 digest")
1059
+ if required_text(payload, "execution_evidence", label="RECOMP response") not in {
1060
+ "pinned_upstream_source",
1061
+ "fake_checkout_plumbing",
1062
+ }:
1063
+ raise AlgorithmOutputError("RECOMP execution evidence is invalid")
1064
+
1065
+
1066
+ def _parse_sentence_scores(raw: Any, state: FrozenAgentState) -> tuple[RECOMPSentenceScore, ...]:
1067
+ rows = mapping_sequence(raw, label="RECOMP sentence_scores")
1068
+ if len(rows) != len(state.context):
1069
+ raise AlgorithmOutputError("RECOMP score count must match fixed candidate sentences")
1070
+ result: list[RECOMPSentenceScore] = []
1071
+ for row_index, (row, source) in enumerate(zip(rows, state.context, strict=True)):
1072
+ label = f"RECOMP sentence score {row_index}"
1073
+ sentence_id = required_text(row, "sentence_id", label=label)
1074
+ document_id = required_text(row, "document_id", label=label)
1075
+ index = nonnegative_integer(row.get("document_index"), label=f"{label} document_index")
1076
+ if sentence_id != source.block_id or document_id != source.block_id or index != row_index:
1077
+ raise AlgorithmOutputError("RECOMP fixed candidate ID/order mismatch")
1078
+ sentence_index = nonnegative_integer(
1079
+ row.get("sentence_index"), label=f"{label} sentence_index"
1080
+ )
1081
+ char_start = nonnegative_integer(row.get("char_start"), label=f"{label} char_start")
1082
+ char_end = nonnegative_integer(row.get("char_end"), label=f"{label} char_end")
1083
+ text = required_text(row, "text", label=label)
1084
+ if sentence_index != 0 or char_start != 0 or char_end != len(source.text):
1085
+ raise AlgorithmOutputError("RECOMP fixed candidate offsets changed")
1086
+ if text != source.text:
1087
+ raise AlgorithmOutputError("RECOMP fixed candidate text changed")
1088
+ result.append(
1089
+ RECOMPSentenceScore(
1090
+ sentence_id=sentence_id,
1091
+ document_id=document_id,
1092
+ document_index=index,
1093
+ sentence_index=sentence_index,
1094
+ char_start=char_start,
1095
+ char_end=char_end,
1096
+ text=text,
1097
+ score=finite_number(row.get("score"), label=f"{label} score"),
1098
+ )
1099
+ )
1100
+ return tuple(result)
1101
+
1102
+
1103
+ def _select_sentences(
1104
+ scores: tuple[RECOMPSentenceScore, ...], controls: RECOMPExtractiveControls
1105
+ ) -> tuple[RECOMPSentenceScore, ...]:
1106
+ eligible = tuple(
1107
+ item
1108
+ for item in scores
1109
+ if controls.minimum_score is None or item.score >= controls.minimum_score
1110
+ )
1111
+ ranked = sorted(eligible, key=lambda item: (-item.score, item.source_order))
1112
+ if controls.max_sentences != -1:
1113
+ ranked = ranked[: controls.max_sentences]
1114
+ if controls.output_order == "source_order":
1115
+ ranked.sort(key=lambda item: item.source_order)
1116
+ return tuple(ranked)
1117
+
1118
+
1119
+ __all__ = [
1120
+ "RECOMP_ABSTRACTIVE_INPUT_TEMPLATE",
1121
+ "RECOMP_ABSTRACTIVE_INPUT_TEMPLATE_SHA256",
1122
+ "RECOMP_CANDIDATE_UNIT_KIND",
1123
+ "RECOMP_UPSTREAM_COMMIT",
1124
+ "RECOMPAbstractiveControls",
1125
+ "RECOMPAbstractiveExternal",
1126
+ "RECOMPExtractiveControls",
1127
+ "RECOMPExtractiveExternal",
1128
+ "RECOMPSentenceScore",
1129
+ ]