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
agentinputlab/api.py ADDED
@@ -0,0 +1,368 @@
1
+ """Small Python API and authenticated loopback HTTP transport for the SDK.
2
+
3
+ No experiment archives, remote imports, arbitrary commands or client-supplied
4
+ backend URLs are accepted. Operators configure dependencies in Python.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import hmac
11
+ import inspect
12
+ import json
13
+ import os
14
+ import threading
15
+ from collections.abc import Callable, Mapping
16
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
17
+ from typing import Any
18
+
19
+ from agentinputlab import ContextBlock, ExecutionBudget, FrozenAgentState, Message
20
+ from agentinputlab.benchmarks import BenchmarkRegistry
21
+ from agentinputlab.contracts import InterventionContext
22
+ from agentinputlab.contracts.errors import (
23
+ AgentInputLabError,
24
+ AlgorithmOutputError,
25
+ AlgorithmUnavailableError,
26
+ BudgetExceededError,
27
+ CapabilityError,
28
+ RegistryError,
29
+ ValidationError,
30
+ )
31
+ from agentinputlab.contracts.serialization import to_jsonable
32
+ from agentinputlab.registry import AlgorithmRegistry
33
+ from agentinputlab.runner import CompositeInterventionRunner, InterventionRunner
34
+ from agentinputlab.selectors import NoOpIntervention
35
+
36
+
37
+ def create_intervention(release_id: str, **parameters: Any) -> Any:
38
+ """Instantiate a registered profile; optional runtime assets remain explicit.
39
+
40
+ Parameters use the adapter's original Python signature. This does not turn
41
+ composite agents into input-patch methods or assert benchmark eligibility.
42
+ """
43
+ if release_id == "baseline.no_op":
44
+ return NoOpIntervention(**parameters)
45
+ factory = AlgorithmRegistry.default().resolve_entrypoint(release_id)
46
+ signature = inspect.signature(factory)
47
+ if "release_id" in signature.parameters:
48
+ parameters["release_id"] = release_id
49
+ signature.bind(**parameters)
50
+ result = factory(**parameters)
51
+ actual = getattr(result, "release_id", release_id)
52
+ if actual != release_id:
53
+ raise ValidationError(
54
+ f"registered profile {release_id} constructed a different release: {actual}"
55
+ )
56
+ return result
57
+
58
+
59
+ def run_intervention(
60
+ intervention: Any, state: FrozenAgentState, context: InterventionContext | None = None
61
+ ) -> dict[str, Any]:
62
+ """Run one patch intervention, preserving the native trace, budget and errors.
63
+
64
+ A successful patch is NOT a benchmark score. Continue the patched state in
65
+ the benchmark's native runtime and official scorer to obtain an endpoint.
66
+ """
67
+ record = InterventionRunner().run(intervention, state, context or InterventionContext())
68
+ return {
69
+ "record": to_jsonable(record),
70
+ "state": to_jsonable(record.patch.apply(state)),
71
+ "scored": False,
72
+ "benchmark_score": None,
73
+ }
74
+
75
+
76
+ def run_composite_intervention(
77
+ intervention: Any, state: FrozenAgentState, context: InterventionContext | None = None
78
+ ) -> dict[str, Any]:
79
+ """Run an answer-producing architecture with observed calls and cost.
80
+
81
+ The result is not a state patch or a benchmark score. The independent
82
+ benchmark provider/scorer still evaluates the returned final answer.
83
+ """
84
+ record = CompositeInterventionRunner().run(
85
+ intervention, state, context or InterventionContext()
86
+ )
87
+ return {
88
+ "record": to_jsonable(record),
89
+ "final_answer": record.result.final_answer,
90
+ "evidence": to_jsonable(record.result.evidence),
91
+ "scored": False,
92
+ "benchmark_score": None,
93
+ }
94
+
95
+
96
+ def catalog() -> dict[str, Any]:
97
+ registry = AlgorithmRegistry.default()
98
+ return {
99
+ "methods": [m.to_dict() for m in registry.list_methods()],
100
+ "benchmarks": [to_jsonable(b) for b in BenchmarkRegistry.default().list()],
101
+ "note": "Inventory is not a claim that every method/dataset/model combination is supported or tested.",
102
+ }
103
+
104
+
105
+ def _state(value: Any) -> FrozenAgentState:
106
+ if not isinstance(value, dict):
107
+ raise ValidationError("state must be an object")
108
+ allowed = {
109
+ "query",
110
+ "task_id",
111
+ "instruction",
112
+ "context",
113
+ "messages",
114
+ "tool_names",
115
+ "metadata",
116
+ "budget",
117
+ }
118
+ if value.keys() - allowed:
119
+ raise ValidationError("unknown state fields")
120
+ for key in ("query", "task_id", "instruction"):
121
+ if key in value and not isinstance(value[key], str):
122
+ raise ValidationError(f"{key} must be a string")
123
+ for key in ("context", "messages", "tool_names"):
124
+ if key in value and not isinstance(value[key], list):
125
+ raise ValidationError(f"{key} must be an array")
126
+ for key in ("metadata", "budget"):
127
+ if key in value and not isinstance(value[key], dict):
128
+ raise ValidationError(f"{key} must be an object")
129
+ try:
130
+ return FrozenAgentState(
131
+ **{
132
+ **value,
133
+ "context": tuple(ContextBlock(**x) for x in value.get("context", [])),
134
+ "messages": tuple(Message(**x) for x in value.get("messages", [])),
135
+ "budget": ExecutionBudget(**value.get("budget", {})),
136
+ }
137
+ )
138
+ except (TypeError, AttributeError) as exc:
139
+ raise ValidationError("invalid nested state fields") from exc
140
+
141
+
142
+ class ToolkitService:
143
+ """Operator-owned factories and contexts; clients cannot inject Python code.
144
+
145
+ All Python profiles remain available through create_intervention. HTTP only
146
+ exposes explicitly configured patch profiles. Construct a fresh context per
147
+ request; do not share a mutable budget ledger between calls.
148
+ """
149
+
150
+ def __init__(
151
+ self,
152
+ factories: Mapping[str, Callable[[], Any]] | None = None,
153
+ context_factory: Callable[[int], InterventionContext] | None = None,
154
+ ):
155
+ self.factories = dict(
156
+ factories
157
+ if factories is not None
158
+ else {
159
+ "baseline.no_op": NoOpIntervention,
160
+ "plan.plan_and_solve.ps_prompt": lambda: create_intervention(
161
+ "plan.plan_and_solve.ps_prompt"
162
+ ),
163
+ }
164
+ )
165
+ self.context_factory = context_factory or (lambda seed: InterventionContext(seed=seed))
166
+
167
+ def execute(self, body: Any) -> dict[str, Any]:
168
+ if not isinstance(body, dict) or body.keys() - {"release_id", "state", "seed"}:
169
+ raise ValidationError("expected release_id, state and optional seed")
170
+ rid = body.get("release_id")
171
+ if not isinstance(rid, str):
172
+ raise ValidationError("release_id must be a string")
173
+ if rid not in self.factories:
174
+ raise AlgorithmUnavailableError("profile is not configured on this server")
175
+ seed = body.get("seed", 0)
176
+ if isinstance(seed, bool) or not isinstance(seed, int) or seed < 0:
177
+ raise ValidationError("seed must be a non-negative integer")
178
+ state = _state(body.get("state"))
179
+ return run_intervention(self.factories[rid](), state, self.context_factory(seed))
180
+
181
+
182
+ def make_server(service: ToolkitService, *, token: str, port: int = 8765) -> ThreadingHTTPServer:
183
+ """Authenticated loopback-only development service, not a public web host.
184
+
185
+ Bounded request bodies and one active execution. Put a production ASGI
186
+ deployment with TLS, quotas and job persistence in front for shared use.
187
+ Client timeouts do not cancel running model calls; there is no auto retry.
188
+ """
189
+ if not isinstance(token, str) or len(token) < 24 or not token.isascii():
190
+ raise ValueError("API token must be at least 24 ASCII characters")
191
+ lock = threading.Lock()
192
+
193
+ class Handler(BaseHTTPRequestHandler):
194
+ def setup(self):
195
+ super().setup()
196
+ self.connection.settimeout(30)
197
+
198
+ def log_message(self, *_args):
199
+ pass # Never log bearer tokens, user prompts or upstream errors.
200
+
201
+ def send_json(self, status: int, value: Any):
202
+ data = json.dumps(value, allow_nan=False).encode()
203
+ self.send_response(status)
204
+ self.send_header("Content-Type", "application/json")
205
+ self.send_header("Content-Length", str(len(data)))
206
+ self.send_header("Cache-Control", "no-store")
207
+ self.send_header("X-Content-Type-Options", "nosniff")
208
+ self.end_headers()
209
+ self.wfile.write(data)
210
+
211
+ def authorized(self):
212
+ given = self.headers.get("Authorization", "").encode()
213
+ if not hmac.compare_digest(given, ("Bearer " + token).encode()):
214
+ self.send_json(
215
+ 401,
216
+ {"error": {"code": "unauthorized", "message": "A valid API token is required"}},
217
+ )
218
+ return False
219
+ return True
220
+
221
+ def do_GET(self):
222
+ if not self.authorized():
223
+ return
224
+ if self.path == "/v1/catalog":
225
+ self.send_json(
226
+ 200, {"data": {**catalog(), "configured_releases": sorted(service.factories)}}
227
+ )
228
+ elif self.path == "/v1/health":
229
+ self.send_json(200, {"data": {"status": "ok", "protocol": "agentinputlab.v1"}})
230
+ else:
231
+ self.send_json(404, {"error": {"code": "not_found", "message": "Unknown resource"}})
232
+
233
+ def do_POST(self):
234
+ if not self.authorized():
235
+ return
236
+ if self.path != "/v1/interventions":
237
+ self.send_json(404, {"error": {"code": "not_found", "message": "Unknown resource"}})
238
+ return
239
+ if self.headers.get("Transfer-Encoding"):
240
+ self.send_json(
241
+ 400,
242
+ {
243
+ "error": {
244
+ "code": "invalid_body",
245
+ "message": "Chunked bodies are unsupported",
246
+ }
247
+ },
248
+ )
249
+ return
250
+ try:
251
+ length = int(self.headers.get("Content-Length", "-1"))
252
+ except ValueError:
253
+ length = -1
254
+ if length < 0 or length > 1024 * 1024:
255
+ self.send_json(
256
+ 413 if length > 0 else 411,
257
+ {
258
+ "error": {
259
+ "code": "body_size",
260
+ "message": "A body of at most 1 MiB is required",
261
+ }
262
+ },
263
+ )
264
+ return
265
+ if self.headers.get_content_type() != "application/json":
266
+ self.send_json(
267
+ 415, {"error": {"code": "content_type", "message": "Use application/json"}}
268
+ )
269
+ return
270
+ if not lock.acquire(blocking=False):
271
+ self.send_json(
272
+ 429,
273
+ {
274
+ "error": {
275
+ "code": "busy",
276
+ "message": "An execution is already active; no attempt started",
277
+ }
278
+ },
279
+ )
280
+ return
281
+ try:
282
+ body = json.loads(
283
+ self.rfile.read(length),
284
+ parse_constant=lambda _: (_ for _ in ()).throw(ValueError()),
285
+ )
286
+ result = service.execute(body)
287
+ self.send_json(200, {"data": result})
288
+ except (json.JSONDecodeError, UnicodeDecodeError, ValueError) as exc:
289
+ # Output errors inherit ValueError through ValidationError.
290
+ code = (
291
+ "algorithm_output"
292
+ if isinstance(exc, AlgorithmOutputError)
293
+ else "invalid_request"
294
+ )
295
+ self.send_json(
296
+ 422 if code == "algorithm_output" else 400,
297
+ {
298
+ "error": {
299
+ "code": code,
300
+ "message": "Invalid output"
301
+ if code == "algorithm_output"
302
+ else "Invalid request schema",
303
+ }
304
+ },
305
+ )
306
+ except (AlgorithmUnavailableError, CapabilityError, RegistryError):
307
+ self.send_json(
308
+ 422,
309
+ {
310
+ "error": {
311
+ "code": "unavailable",
312
+ "message": "Required profile dependencies are not configured",
313
+ }
314
+ },
315
+ )
316
+ except BudgetExceededError:
317
+ self.send_json(
318
+ 422,
319
+ {
320
+ "error": {
321
+ "code": "budget_exhausted",
322
+ "message": "Execution budget exhausted",
323
+ }
324
+ },
325
+ )
326
+ except AgentInputLabError:
327
+ self.send_json(
328
+ 422,
329
+ {
330
+ "error": {
331
+ "code": "execution_failed",
332
+ "message": "Execution rejected; no score was fabricated",
333
+ }
334
+ },
335
+ )
336
+ except Exception:
337
+ self.send_json(
338
+ 500,
339
+ {
340
+ "error": {
341
+ "code": "internal_error",
342
+ "message": "Execution failed; inspect the operator environment",
343
+ }
344
+ },
345
+ )
346
+ finally:
347
+ lock.release()
348
+
349
+ return ThreadingHTTPServer(("127.0.0.1", port), Handler)
350
+
351
+
352
+ def main() -> None:
353
+ parser = argparse.ArgumentParser(description="Authenticated loopback AgentInputLab SDK service")
354
+ parser.add_argument("--port", type=int, default=8765)
355
+ args = parser.parse_args()
356
+ server = make_server(
357
+ ToolkitService(), token=os.environ.get("AGENTINPUTLAB_API_TOKEN", ""), port=args.port
358
+ )
359
+ try:
360
+ server.serve_forever()
361
+ except KeyboardInterrupt:
362
+ pass
363
+ finally:
364
+ server.server_close()
365
+
366
+
367
+ if __name__ == "__main__":
368
+ main()
@@ -0,0 +1,15 @@
1
+ """Artifact schemas and checksum-verified local storage."""
2
+
3
+ from .json_file import load_pinned_json_mapping
4
+ from .manifest import AcquisitionPolicy, ArtifactKind, ArtifactManifest, UpstreamLock
5
+ from .store import ArtifactStore, sha256_file
6
+
7
+ __all__ = [
8
+ "AcquisitionPolicy",
9
+ "ArtifactKind",
10
+ "ArtifactManifest",
11
+ "ArtifactStore",
12
+ "UpstreamLock",
13
+ "load_pinned_json_mapping",
14
+ "sha256_file",
15
+ ]
@@ -0,0 +1,104 @@
1
+ """Bounded checksum-pinned JSON loading without pickle or executable imports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import math
8
+ import os
9
+ import stat
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from agentinputlab.contracts.errors import ArtifactError
14
+
15
+
16
+ def _unique_object(pairs):
17
+ result = {}
18
+ for key, value in pairs:
19
+ if key in result:
20
+ raise ValueError("duplicate JSON object keys")
21
+ result[key] = value
22
+ return result
23
+
24
+
25
+ def _finite_json(_value):
26
+ raise ValueError("non-finite JSON numbers")
27
+
28
+
29
+ def _finite_float(value):
30
+ number = float(value)
31
+ if not math.isfinite(number):
32
+ raise ValueError("non-finite JSON numbers")
33
+ return number
34
+
35
+
36
+ def load_pinned_json_mapping(
37
+ path: str | Path,
38
+ *,
39
+ expected_sha256: str,
40
+ max_bytes: int = 4 * 1024 * 1024,
41
+ ) -> dict[str, Any]:
42
+ """Read one regular UTF-8 JSON object under a caller-trusted byte checksum.
43
+
44
+ The checksum must come from a trusted receipt, not be computed from an
45
+ untrusted file immediately before loading. It proves byte identity, not
46
+ authorship, source-license permission, or the truth of recorded results.
47
+ """
48
+
49
+ if (
50
+ not isinstance(expected_sha256, str)
51
+ or len(expected_sha256) != 64
52
+ or any(c not in "0123456789abcdef" for c in expected_sha256.lower())
53
+ ):
54
+ raise ArtifactError("expected_sha256 must be a SHA-256 hex digest")
55
+ if type(max_bytes) is not int or max_bytes < 1:
56
+ raise ArtifactError("artifact JSON max_bytes must be a positive integer")
57
+ source = Path(path).expanduser()
58
+ read_failed = False
59
+ try:
60
+ before = source.lstat()
61
+ if not stat.S_ISREG(before.st_mode) or before.st_size > max_bytes:
62
+ raise ArtifactError("artifact JSON must be a bounded regular file, not a symlink")
63
+ descriptor = os.open(
64
+ source, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0)
65
+ )
66
+ with os.fdopen(descriptor, "rb") as handle:
67
+ opened = os.fstat(handle.fileno())
68
+ if not stat.S_ISREG(opened.st_mode) or (before.st_dev, before.st_ino) != (
69
+ opened.st_dev,
70
+ opened.st_ino,
71
+ ):
72
+ raise ArtifactError("artifact JSON file changed while opening")
73
+ raw = handle.read(max_bytes + 1)
74
+ after = os.fstat(handle.fileno())
75
+ if (opened.st_size, opened.st_mtime_ns, opened.st_ctime_ns) != (
76
+ after.st_size,
77
+ after.st_mtime_ns,
78
+ after.st_ctime_ns,
79
+ ):
80
+ raise ArtifactError("artifact JSON file changed while reading")
81
+ except OSError:
82
+ read_failed = True
83
+ if read_failed:
84
+ raise ArtifactError("artifact JSON file is unavailable")
85
+ if len(raw) > max_bytes:
86
+ raise ArtifactError("artifact JSON exceeds max_bytes")
87
+ if hashlib.sha256(raw).hexdigest() != expected_sha256.lower():
88
+ raise ArtifactError("artifact JSON checksum mismatch")
89
+ invalid_json = False
90
+ value = None
91
+ try:
92
+ value = json.loads(
93
+ raw.decode("utf-8"),
94
+ object_pairs_hook=_unique_object,
95
+ parse_constant=_finite_json,
96
+ parse_float=_finite_float,
97
+ )
98
+ except (UnicodeError, ValueError, RecursionError):
99
+ invalid_json = True
100
+ if invalid_json or not isinstance(value, dict):
101
+ raise ArtifactError(
102
+ "artifact must contain a valid UTF-8 JSON object with unique keys and finite values"
103
+ )
104
+ return value
@@ -0,0 +1,98 @@
1
+ """Artifact and upstream-source identity schemas."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from collections.abc import Mapping
7
+ from dataclasses import dataclass, field
8
+ from typing import Any
9
+
10
+ from agentinputlab.contracts.enums import StrEnum
11
+ from agentinputlab.contracts.errors import ValidationError
12
+ from agentinputlab.contracts.serialization import freeze_mapping
13
+
14
+ _SHA256 = re.compile(r"^[0-9a-f]{64}$")
15
+ _GIT_SHA = re.compile(r"^[0-9a-f]{40}$")
16
+ _SAFE_SEGMENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
17
+
18
+
19
+ class ArtifactKind(StrEnum):
20
+ DATASET = "dataset"
21
+ MODEL = "model"
22
+ INDEX = "index"
23
+ CACHE = "cache"
24
+ FEATURE = "feature"
25
+ PROMPT = "prompt"
26
+ TRACE = "trace"
27
+ RECIPE_OUTPUT = "recipe_output"
28
+
29
+
30
+ class AcquisitionPolicy(StrEnum):
31
+ BUNDLED = "bundled"
32
+ DOWNLOAD = "download"
33
+ USER_PROVIDED = "user_provided"
34
+ GATED = "gated"
35
+ EXTERNAL_MOUNT = "external_mount"
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class UpstreamLock:
40
+ repository: str
41
+ commit: str
42
+ license_id: str
43
+ vendoring_allowed: bool
44
+ subpath: str = ""
45
+ branch: str = ""
46
+
47
+ def __post_init__(self) -> None:
48
+ if self.commit and not _GIT_SHA.fullmatch(self.commit):
49
+ raise ValidationError("upstream git commit must be a full 40-character SHA")
50
+ if self.license_id == "NOASSERTION" and self.vendoring_allowed:
51
+ raise ValidationError("NOASSERTION source cannot be marked vendoring_allowed")
52
+
53
+
54
+ @dataclass(frozen=True, slots=True)
55
+ class ArtifactManifest:
56
+ artifact_id: str
57
+ kind: ArtifactKind
58
+ revision: str
59
+ sha256: str
60
+ acquisition: AcquisitionPolicy
61
+ license_id: str
62
+ source_uri: str = ""
63
+ size_bytes: int | None = None
64
+ lineage: Mapping[str, str] = field(default_factory=dict)
65
+ metadata: Mapping[str, Any] = field(default_factory=dict)
66
+
67
+ def __post_init__(self) -> None:
68
+ if not _SAFE_SEGMENT.fullmatch(self.artifact_id) or ".." in self.artifact_id:
69
+ raise ValidationError("artifact_id must be a flat safe identifier")
70
+ if not _SAFE_SEGMENT.fullmatch(self.revision) or ".." in self.revision:
71
+ raise ValidationError("artifact revision must be a flat safe identifier")
72
+ if self.sha256 and not _SHA256.fullmatch(self.sha256):
73
+ raise ValidationError("artifact sha256 must contain 64 lowercase hex characters")
74
+ if self.acquisition is AcquisitionPolicy.DOWNLOAD:
75
+ if not self.sha256:
76
+ raise ValidationError("download artifacts require a pinned sha256")
77
+ if not self.source_uri:
78
+ raise ValidationError("download artifacts require a source_uri")
79
+ if self.size_bytes is not None and self.size_bytes < 0:
80
+ raise ValidationError("artifact size must be non-negative")
81
+ lineage = dict(self.lineage)
82
+ if any(
83
+ not isinstance(key, str)
84
+ or not key.strip()
85
+ or not isinstance(value, str)
86
+ or not value.strip()
87
+ for key, value in lineage.items()
88
+ ):
89
+ raise ValidationError("artifact lineage requires non-empty string keys and values")
90
+ try:
91
+ frozen_lineage = freeze_mapping(lineage)
92
+ frozen_metadata = freeze_mapping(self.metadata)
93
+ except TypeError as exc:
94
+ raise ValidationError(
95
+ f"artifact metadata must be canonically serializable: {exc}"
96
+ ) from exc
97
+ object.__setattr__(self, "lineage", frozen_lineage)
98
+ object.__setattr__(self, "metadata", frozen_metadata)