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,9 @@
1
+ """Small checksum helpers shared by download and preprocessing pipelines."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+
7
+
8
+ def sha256_bytes(content: bytes) -> str:
9
+ return hashlib.sha256(content).hexdigest()
@@ -0,0 +1,235 @@
1
+ """Fail-closed artifact acquisition with an injected hardened transport.
2
+
3
+ The core package deliberately does not ship a generic ``urlopen`` transport.
4
+ An injected transport receives already-resolved targets, must connect to one of
5
+ those addresses while validating TLS for the original hostname, and must not
6
+ follow redirects itself. AgentInputLab validates every redirect hop and the
7
+ reported peer before accepting bytes.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import ipaddress
13
+ import socket
14
+ import urllib.parse
15
+ from collections.abc import Iterable, Mapping
16
+ from contextlib import AbstractContextManager
17
+ from dataclasses import dataclass
18
+ from typing import Protocol, runtime_checkable
19
+
20
+ from agentinputlab.artifacts import ArtifactManifest, ArtifactStore
21
+ from agentinputlab.contracts.errors import ArtifactError
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class DownloadPolicy:
26
+ allow_network: bool = False
27
+ allowed_hosts: tuple[str, ...] = ()
28
+ allow_private_addresses: bool = False
29
+ max_bytes: int = 2 * 1024 * 1024 * 1024
30
+ timeout_seconds: float = 120.0
31
+ max_redirects: int = 3
32
+
33
+ def __post_init__(self) -> None:
34
+ normalized = tuple(sorted({host.strip().lower() for host in self.allowed_hosts if host}))
35
+ object.__setattr__(self, "allowed_hosts", normalized)
36
+ if self.max_bytes < 1:
37
+ raise ArtifactError("download max_bytes must be positive")
38
+ if self.timeout_seconds <= 0:
39
+ raise ArtifactError("download timeout_seconds must be positive")
40
+ if self.max_redirects < 0:
41
+ raise ArtifactError("download max_redirects must be non-negative")
42
+
43
+
44
+ @dataclass(frozen=True, slots=True)
45
+ class ValidatedDownloadTarget:
46
+ url: str
47
+ hostname: str
48
+ port: int
49
+ resolved_addresses: tuple[str, ...]
50
+
51
+
52
+ @dataclass(frozen=True, slots=True)
53
+ class DownloadResponse:
54
+ status_code: int
55
+ headers: Mapping[str, str]
56
+ body: bytes
57
+ peer_address: str
58
+
59
+
60
+ @dataclass(frozen=True, slots=True)
61
+ class StreamingDownloadResponse:
62
+ status_code: int
63
+ headers: Mapping[str, str]
64
+ chunks: Iterable[bytes]
65
+ peer_address: str
66
+
67
+
68
+ class StreamingDownloadTransport(Protocol):
69
+ def stream(
70
+ self, target: ValidatedDownloadTarget, *, timeout_seconds: float, max_bytes: int
71
+ ) -> AbstractContextManager[StreamingDownloadResponse]:
72
+ """Open one DNS-pinned hop; close it even when the consumer stops early."""
73
+
74
+
75
+ @runtime_checkable
76
+ class DownloadTransport(Protocol):
77
+ def request(
78
+ self,
79
+ target: ValidatedDownloadTarget,
80
+ *,
81
+ timeout_seconds: float,
82
+ max_bytes: int,
83
+ ) -> DownloadResponse:
84
+ """Fetch one hop without redirects or decompression expansion."""
85
+
86
+
87
+ def validate_download_url(url: str, policy: DownloadPolicy) -> ValidatedDownloadTarget:
88
+ if not policy.allow_network:
89
+ raise ArtifactError("network acquisition is disabled")
90
+ if not policy.allowed_hosts:
91
+ raise ArtifactError("network acquisition requires a non-empty host allowlist")
92
+ parsed = urllib.parse.urlsplit(url)
93
+ if parsed.scheme != "https" or not parsed.hostname:
94
+ raise ArtifactError("artifact URLs must use absolute HTTPS")
95
+ if parsed.username is not None or parsed.password is not None:
96
+ raise ArtifactError("artifact URLs must not contain user information")
97
+ hostname = parsed.hostname.lower()
98
+ if hostname not in policy.allowed_hosts:
99
+ raise ArtifactError(f"host is not allowlisted: {hostname}")
100
+ try:
101
+ port = parsed.port or 443
102
+ except ValueError as exc:
103
+ raise ArtifactError("artifact URL contains an invalid port") from exc
104
+ try:
105
+ addresses = {
106
+ info[4][0].split("%", 1)[0]
107
+ for info in socket.getaddrinfo(hostname, port, type=socket.SOCK_STREAM)
108
+ }
109
+ except socket.gaierror as exc:
110
+ raise ArtifactError(f"cannot resolve artifact host: {hostname}") from exc
111
+ if not addresses:
112
+ raise ArtifactError(f"artifact host resolved to no addresses: {hostname}")
113
+ if not policy.allow_private_addresses:
114
+ for address in addresses:
115
+ ip = ipaddress.ip_address(address)
116
+ if not ip.is_global:
117
+ raise ArtifactError(f"artifact host resolves to disallowed address: {address}")
118
+ return ValidatedDownloadTarget(
119
+ url=urllib.parse.urlunsplit(parsed),
120
+ hostname=hostname,
121
+ port=port,
122
+ resolved_addresses=tuple(sorted(addresses)),
123
+ )
124
+
125
+
126
+ def download_artifact(
127
+ manifest: ArtifactManifest,
128
+ store: ArtifactStore,
129
+ *,
130
+ policy: DownloadPolicy,
131
+ transport: DownloadTransport | None = None,
132
+ ) -> str:
133
+ if not manifest.sha256:
134
+ raise ArtifactError("downloaded artifacts require a pinned SHA-256")
135
+ if transport is None:
136
+ raise ArtifactError("network acquisition requires an explicit hardened transport")
137
+ target = validate_download_url(manifest.source_uri, policy)
138
+ response: DownloadResponse | None = None
139
+ for redirect_count in range(policy.max_redirects + 1):
140
+ response = transport.request(
141
+ target,
142
+ timeout_seconds=policy.timeout_seconds,
143
+ max_bytes=policy.max_bytes,
144
+ )
145
+ _validate_peer(response.peer_address, target)
146
+ if 300 <= response.status_code < 400:
147
+ if redirect_count >= policy.max_redirects:
148
+ raise ArtifactError("artifact download exceeded redirect limit")
149
+ location = _header(response.headers, "location")
150
+ if not location:
151
+ raise ArtifactError("artifact redirect is missing Location")
152
+ target = validate_download_url(
153
+ urllib.parse.urljoin(target.url, location),
154
+ policy,
155
+ )
156
+ continue
157
+ if not 200 <= response.status_code < 300:
158
+ raise ArtifactError(f"artifact download returned HTTP {response.status_code}")
159
+ break
160
+ if response is None: # pragma: no cover - loop invariant
161
+ raise ArtifactError("artifact transport returned no response")
162
+ content_length = _header(response.headers, "content-length")
163
+ if content_length is not None:
164
+ try:
165
+ declared_size = int(content_length)
166
+ except ValueError as exc:
167
+ raise ArtifactError("artifact Content-Length is invalid") from exc
168
+ if declared_size < 0 or declared_size > policy.max_bytes:
169
+ raise ArtifactError("artifact exceeds configured size limit")
170
+ if len(response.body) > policy.max_bytes:
171
+ raise ArtifactError("artifact exceeds configured size limit")
172
+ return str(store.put_bytes(manifest, response.body))
173
+
174
+
175
+ def _validate_peer(peer_address: str, target: ValidatedDownloadTarget) -> None:
176
+ try:
177
+ normalized = str(ipaddress.ip_address(peer_address.split("%", 1)[0]))
178
+ except ValueError as exc:
179
+ raise ArtifactError("download transport reported an invalid peer address") from exc
180
+ allowed = {str(ipaddress.ip_address(address)) for address in target.resolved_addresses}
181
+ if normalized not in allowed:
182
+ raise ArtifactError("download peer does not match the validated DNS result")
183
+
184
+
185
+ def download_artifact_streamed(
186
+ manifest: ArtifactManifest,
187
+ store: ArtifactStore,
188
+ *,
189
+ policy: DownloadPolicy,
190
+ transport: StreamingDownloadTransport,
191
+ ) -> str:
192
+ """Acquire a pinned file with bounded RAM and validation at every hop."""
193
+ if not manifest.sha256:
194
+ raise ArtifactError("downloaded artifacts require a pinned SHA-256")
195
+ if manifest.size_bytes is not None and manifest.size_bytes > policy.max_bytes:
196
+ raise ArtifactError("artifact exceeds configured size limit before download")
197
+ target = validate_download_url(manifest.source_uri, policy)
198
+ for count in range(policy.max_redirects + 1):
199
+ with transport.stream(
200
+ target, timeout_seconds=policy.timeout_seconds, max_bytes=policy.max_bytes
201
+ ) as response:
202
+ _validate_peer(response.peer_address, target)
203
+ if 300 <= response.status_code < 400:
204
+ if count >= policy.max_redirects:
205
+ raise ArtifactError("artifact download exceeded redirect limit")
206
+ location = _header(response.headers, "location")
207
+ if not location:
208
+ raise ArtifactError("artifact redirect is missing Location")
209
+ target = validate_download_url(urllib.parse.urljoin(target.url, location), policy)
210
+ continue
211
+ if not 200 <= response.status_code < 300:
212
+ raise ArtifactError(f"artifact download returned HTTP {response.status_code}")
213
+ encoding = _header(response.headers, "content-encoding")
214
+ if encoding and encoding.lower() != "identity":
215
+ raise ArtifactError("artifact stream refuses encoded response bodies")
216
+ declared = _header(response.headers, "content-length")
217
+ if declared is not None:
218
+ try:
219
+ size = int(declared)
220
+ except ValueError as exc:
221
+ raise ArtifactError("artifact Content-Length is invalid") from exc
222
+ if size < 0 or size > policy.max_bytes:
223
+ raise ArtifactError("artifact exceeds configured size limit")
224
+ if manifest.size_bytes is not None and size != manifest.size_bytes:
225
+ raise ArtifactError("artifact declared size differs from pinned size")
226
+ return str(store.put_stream(manifest, response.chunks, max_bytes=policy.max_bytes))
227
+ raise ArtifactError("artifact download exceeded redirect limit") # pragma: no cover
228
+
229
+
230
+ def _header(headers: Mapping[str, str], name: str) -> str | None:
231
+ expected = name.lower()
232
+ for key, value in headers.items():
233
+ if key.lower() == expected:
234
+ return str(value)
235
+ return None
@@ -0,0 +1,171 @@
1
+ """Pinned-address HTTPS transport for explicit benchmark acquisition.
2
+
3
+ This transport is opt-in. It connects to one of the addresses validated by
4
+ ``validate_download_url`` while retaining the original hostname for TLS SNI and
5
+ certificate verification. It does not use environment proxies, follow
6
+ redirects, or decompress response bodies.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import http.client
12
+ import socket
13
+ import ssl
14
+ import urllib.parse
15
+ from contextlib import contextmanager
16
+ from dataclasses import dataclass, field
17
+
18
+ from agentinputlab.contracts.errors import ArtifactError
19
+
20
+ from .downloader import DownloadResponse, StreamingDownloadResponse, ValidatedDownloadTarget
21
+
22
+
23
+ class _PinnedHTTPSConnection(http.client.HTTPSConnection):
24
+ def __init__(
25
+ self,
26
+ target: ValidatedDownloadTarget,
27
+ address: str,
28
+ *,
29
+ timeout: float,
30
+ context: ssl.SSLContext,
31
+ ) -> None:
32
+ super().__init__(
33
+ target.hostname,
34
+ target.port,
35
+ timeout=timeout,
36
+ context=context,
37
+ )
38
+ self._validated_address = address
39
+
40
+ def connect(self) -> None:
41
+ raw_socket = socket.create_connection(
42
+ (self._validated_address, self.port),
43
+ self.timeout,
44
+ self.source_address,
45
+ )
46
+ try:
47
+ self.sock = self._context.wrap_socket(
48
+ raw_socket,
49
+ server_hostname=self.host,
50
+ )
51
+ except BaseException:
52
+ raw_socket.close()
53
+ raise
54
+
55
+
56
+ @dataclass(slots=True)
57
+ class PinnedHTTPSDownloadTransport:
58
+ """One-hop stdlib HTTPS client honoring the downloader's DNS pin."""
59
+
60
+ user_agent: str = "AgentInputLab/0.1 benchmark-acquisition"
61
+ ssl_context: ssl.SSLContext = field(default_factory=ssl.create_default_context)
62
+
63
+ def request(
64
+ self,
65
+ target: ValidatedDownloadTarget,
66
+ *,
67
+ timeout_seconds: float,
68
+ max_bytes: int,
69
+ ) -> DownloadResponse:
70
+ with self.stream(target, timeout_seconds=timeout_seconds, max_bytes=max_bytes) as response:
71
+ return DownloadResponse(
72
+ status_code=response.status_code,
73
+ headers=response.headers,
74
+ body=b"".join(response.chunks),
75
+ peer_address=response.peer_address,
76
+ )
77
+
78
+ @contextmanager
79
+ def stream(
80
+ self,
81
+ target: ValidatedDownloadTarget,
82
+ *,
83
+ timeout_seconds: float,
84
+ max_bytes: int,
85
+ ):
86
+ """Stream one validated address, without redirect or body-read retries."""
87
+ if max_bytes < 1:
88
+ raise ArtifactError("download transport size limit must be positive")
89
+ parsed = urllib.parse.urlsplit(target.url)
90
+ request_target = urllib.parse.urlunsplit(("", "", parsed.path or "/", parsed.query, ""))
91
+ host_header = target.hostname if target.port == 443 else f"{target.hostname}:{target.port}"
92
+ failures: list[str] = []
93
+ for address in target.resolved_addresses:
94
+ connection = _PinnedHTTPSConnection(
95
+ target,
96
+ address,
97
+ timeout=timeout_seconds,
98
+ context=self.ssl_context,
99
+ )
100
+ try:
101
+ connection.request(
102
+ "GET",
103
+ request_target,
104
+ headers={
105
+ "Accept": "*/*",
106
+ "Accept-Encoding": "identity",
107
+ "Connection": "close",
108
+ "Host": host_header,
109
+ "User-Agent": self.user_agent,
110
+ },
111
+ )
112
+ if connection.sock is None: # pragma: no cover - http.client invariant
113
+ raise ArtifactError("HTTPS transport did not establish a socket")
114
+ peer_address = str(connection.sock.getpeername()[0])
115
+ response = connection.getresponse()
116
+ headers = {key: value for key, value in response.getheaders()}
117
+ encoding = _header(headers, "content-encoding")
118
+ if encoding and encoding.lower() != "identity":
119
+ raise ArtifactError("download transport refuses encoded response bodies")
120
+ declared = _header(headers, "content-length")
121
+ if declared is not None:
122
+ try:
123
+ declared_size = int(declared)
124
+ except ValueError as exc:
125
+ raise ArtifactError(
126
+ "download response has an invalid Content-Length"
127
+ ) from exc
128
+ if declared_size < 0 or declared_size > max_bytes:
129
+ raise ArtifactError("download response exceeds the transport size limit")
130
+ except ArtifactError:
131
+ connection.close()
132
+ raise
133
+ except (OSError, ssl.SSLError, http.client.HTTPException) as exc:
134
+ connection.close()
135
+ failures.append(f"{address}: {type(exc).__name__}")
136
+ continue
137
+ break
138
+ else:
139
+ detail = ", ".join(failures) if failures else "no validated addresses"
140
+ raise ArtifactError(f"HTTPS transport could not connect ({detail})")
141
+ try:
142
+ yield StreamingDownloadResponse(
143
+ status_code=response.status,
144
+ headers=headers,
145
+ chunks=_bounded_chunks(response, max_bytes=max_bytes),
146
+ peer_address=peer_address,
147
+ )
148
+ except (OSError, ssl.SSLError, http.client.HTTPException) as exc:
149
+ raise ArtifactError(f"artifact stream interrupted ({type(exc).__name__})") from exc
150
+ finally:
151
+ connection.close()
152
+
153
+
154
+ def _bounded_chunks(response: http.client.HTTPResponse, *, max_bytes: int):
155
+ size = 0
156
+ while True:
157
+ chunk = response.read(min(1024 * 1024, max_bytes + 1 - size))
158
+ if not chunk:
159
+ return
160
+ size += len(chunk)
161
+ if size > max_bytes:
162
+ raise ArtifactError("download response exceeds the transport size limit")
163
+ yield chunk
164
+
165
+
166
+ def _header(headers: dict[str, str], name: str) -> str | None:
167
+ expected = name.lower()
168
+ for key, value in headers.items():
169
+ if key.lower() == expected:
170
+ return value
171
+ return None
@@ -0,0 +1,21 @@
1
+ """Versioned index identity used by retrieval balance checks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from agentinputlab.contracts.serialization import content_hash
8
+
9
+
10
+ @dataclass(frozen=True, slots=True)
11
+ class IndexManifest:
12
+ corpus_revision: str
13
+ document_filter_hash: str
14
+ chunker_hash: str
15
+ embedder_revision: str
16
+ index_revision: str
17
+ metric: str
18
+
19
+ @property
20
+ def lineage_hash(self) -> str:
21
+ return content_hash(self)
@@ -0,0 +1,28 @@
1
+ """Dataset split-overlap records used by contamination-aware evaluation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass(frozen=True, slots=True)
10
+ class OverlapRecord:
11
+ left_split: str
12
+ right_split: str
13
+ shared_ids: tuple[str, ...]
14
+
15
+ @property
16
+ def count(self) -> int:
17
+ return len(self.shared_ids)
18
+
19
+
20
+ def detect_id_overlap(
21
+ left_ids: Iterable[str],
22
+ right_ids: Iterable[str],
23
+ *,
24
+ left_split: str,
25
+ right_split: str,
26
+ ) -> OverlapRecord:
27
+ shared = tuple(sorted(set(left_ids) & set(right_ids)))
28
+ return OverlapRecord(left_split, right_split, shared)
@@ -0,0 +1,32 @@
1
+ """Deterministic JSONL preprocessing primitives."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import Callable, Iterable
7
+ from typing import Any
8
+
9
+ from agentinputlab.contracts.serialization import canonical_json
10
+
11
+
12
+ def normalize_records(
13
+ records: Iterable[dict[str, Any]],
14
+ transform: Callable[[dict[str, Any]], dict[str, Any]],
15
+ *,
16
+ id_field: str = "id",
17
+ ) -> tuple[dict[str, Any], ...]:
18
+ normalized = [transform(dict(record)) for record in records]
19
+ if any(id_field not in record for record in normalized):
20
+ raise ValueError(f"all normalized records require {id_field!r}")
21
+ normalized.sort(key=lambda record: str(record[id_field]))
22
+ if len({str(record[id_field]) for record in normalized}) != len(normalized):
23
+ raise ValueError("normalized record IDs must be unique")
24
+ return tuple(normalized)
25
+
26
+
27
+ def to_jsonl(records: Iterable[dict[str, Any]]) -> str:
28
+ return "\n".join(canonical_json(record) for record in records) + "\n"
29
+
30
+
31
+ def from_jsonl(content: str) -> tuple[dict[str, Any], ...]:
32
+ return tuple(json.loads(line) for line in content.splitlines() if line.strip())
@@ -0,0 +1,57 @@
1
+ """Read-only destination-space checks for bounded data preparation.
2
+
3
+ These checks are snapshots, not filesystem reservations. Independent writers can
4
+ consume space later; callers must still bound each write and clean partial data.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import shutil
10
+ from collections.abc import Iterable
11
+ from pathlib import Path
12
+
13
+ from agentinputlab.contracts import ArtifactError, ValidationError
14
+
15
+ DEFAULT_PREPARED_LIMIT = 512 * 1024 * 1024
16
+ DEFAULT_DISK_RESERVE = 1024**3
17
+
18
+
19
+ def positive_byte_limit(value: int, *, name: str) -> int:
20
+ if isinstance(value, bool) or not isinstance(value, int) or value < 1:
21
+ raise ValidationError(f"{name} must be a positive integer")
22
+ return value
23
+
24
+
25
+ def require_storage_capacity(
26
+ allocations: Iterable[tuple[str | Path, int]],
27
+ *,
28
+ reserve_bytes: int = DEFAULT_DISK_RESERVE,
29
+ ) -> None:
30
+ """Check all planned bytes, summing paths on the same destination device.
31
+
32
+ No directory or temporary file is created. A missing destination is checked
33
+ using its nearest existing ancestor, not the current working directory.
34
+ ``required`` is an upper bound on *additional* source/prepared bytes; cached
35
+ sources do not need a second allowance. Unknown or negative sizes are errors.
36
+ """
37
+ positive_byte_limit(reserve_bytes, name="reserve_bytes")
38
+ devices: dict[int, tuple[Path, int]] = {}
39
+ for destination, required in allocations:
40
+ if isinstance(required, bool) or not isinstance(required, int) or required < 0:
41
+ raise ValidationError("planned storage bytes must be a non-negative integer")
42
+ ancestor = Path(destination).expanduser().resolve()
43
+ while not ancestor.exists():
44
+ ancestor = ancestor.parent
45
+ if not ancestor.is_dir():
46
+ raise ArtifactError(f"storage destination ancestor is not a directory: {ancestor}")
47
+ device = ancestor.stat().st_dev
48
+ previous = devices.get(device, (ancestor, 0))
49
+ devices[device] = (previous[0], previous[1] + required)
50
+ for ancestor, required in devices.values():
51
+ free = shutil.disk_usage(ancestor).free
52
+ if free < required + reserve_bytes:
53
+ raise ArtifactError(
54
+ f"insufficient storage at {ancestor}: need {required:,} additional bytes "
55
+ f"plus {reserve_bytes:,} reserve, found {free:,} free; "
56
+ "defer this source or use a source-side bounded sample"
57
+ )