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,1373 @@
1
+ #!/usr/bin/env python3
2
+ """Pinned JSONL driver for the original IRCoT controller loop."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import contextlib
7
+ import copy
8
+ import difflib
9
+ import hashlib
10
+ import importlib
11
+ import io
12
+ import json
13
+ import math
14
+ import os
15
+ import re
16
+ import subprocess
17
+ import sys
18
+ import types
19
+ from collections.abc import Iterator, Mapping, Sequence
20
+ from dataclasses import dataclass
21
+ from pathlib import Path
22
+ from typing import Any, TextIO
23
+
24
+ UPSTREAM_COMMIT = "3c1820f698eea5eeddb4fba3c56b64c961e063e4"
25
+ PROTOCOL_VERSION = "agentinputlab.ircot.v1"
26
+ MANIFEST_SCHEMA = "agentinputlab.ircot.compatibility.v1"
27
+ UPSTREAM_ENV = "AGENTINPUTLAB_UPSTREAM_ROOT"
28
+ PROFILE_ENV = "AGENTINPUTLAB_IRCOT_PROFILE"
29
+ DEFAULT_MANIFEST = Path(__file__).with_name("compatibility_manifest.json")
30
+ DEFAULT_PROFILE = "fixture.tiny.frozen.v1"
31
+ MAX_REQUEST_BYTES = 2 * 1024 * 1024
32
+ _REQUEST_FIELDS = frozenset(
33
+ {
34
+ "request_id",
35
+ "state_hash",
36
+ "task_id",
37
+ "question",
38
+ "controls",
39
+ "generator_fingerprint",
40
+ "retriever_fingerprint",
41
+ "corpus_revision",
42
+ "index_revision",
43
+ "prompt_hash",
44
+ "upstream_commit",
45
+ "protocol_version",
46
+ }
47
+ )
48
+ _CONTROL_FIELDS = frozenset(
49
+ {
50
+ "per_step_top_k",
51
+ "max_steps",
52
+ "max_total_documents",
53
+ "max_output_tokens",
54
+ "deduplicate",
55
+ "reset_policy",
56
+ }
57
+ )
58
+ _SECRET_KEY_PARTS = (
59
+ "api_key",
60
+ "apikey",
61
+ "authorization",
62
+ "credential",
63
+ "password",
64
+ "secret",
65
+ )
66
+ _SECRET_TOKEN_KEYS = frozenset(
67
+ {
68
+ "token",
69
+ "access_token",
70
+ "auth_token",
71
+ "bearer_token",
72
+ "refresh_token",
73
+ "session_token",
74
+ }
75
+ )
76
+
77
+
78
+ class DriverError(RuntimeError):
79
+ def __init__(self, code: str, message: str, *, pre_execution: bool = True) -> None:
80
+ super().__init__(message)
81
+ self.code = code
82
+ self.public_message = message
83
+ self.pre_execution = pre_execution
84
+
85
+
86
+ @dataclass(frozen=True, slots=True)
87
+ class Controls:
88
+ per_step_top_k: int
89
+ max_steps: int
90
+ max_total_documents: int
91
+ max_output_tokens: int
92
+ deduplicate: bool
93
+ reset_policy: str
94
+
95
+
96
+ @dataclass(frozen=True, slots=True)
97
+ class Request:
98
+ request_id: str
99
+ state_hash: str
100
+ task_id: str
101
+ question: str
102
+ controls: Controls
103
+ generator_fingerprint: Mapping[str, Any]
104
+ retriever_fingerprint: Mapping[str, Any]
105
+ corpus_revision: str
106
+ index_revision: str
107
+ prompt_hash: str
108
+
109
+
110
+ @dataclass(frozen=True, slots=True)
111
+ class Checkout:
112
+ root: Path
113
+ worker_root: Path
114
+ manifest: Mapping[str, Any]
115
+ manifest_sha256: str
116
+ profile_id: str
117
+ profile: Mapping[str, Any]
118
+ config_path: Path
119
+ prompt_path: Path
120
+ bindings_path: Path | None
121
+
122
+
123
+ @dataclass(frozen=True, slots=True)
124
+ class Snapshot:
125
+ question: str
126
+ titles: tuple[str, ...]
127
+ paragraphs: tuple[str, ...]
128
+ generated_sentences: tuple[str, ...]
129
+
130
+
131
+ @dataclass(frozen=True, slots=True)
132
+ class ControllerEvent:
133
+ command: str
134
+ role: str
135
+ before: Snapshot
136
+ after: Snapshot
137
+ retrieval_rounds: tuple[Mapping[str, Any], ...]
138
+
139
+
140
+ class _BoundedCapture(io.TextIOBase):
141
+ def __init__(self, limit: int = 128 * 1024) -> None:
142
+ self.limit = limit
143
+ self.size = 0
144
+ self._digest = hashlib.sha256()
145
+
146
+ def writable(self) -> bool:
147
+ return True
148
+
149
+ def write(self, value: str) -> int:
150
+ encoded = value.encode("utf-8", errors="replace")
151
+ self.size += len(encoded)
152
+ if self.size > self.limit:
153
+ raise DriverError(
154
+ "UPSTREAM_OUTPUT_LIMIT",
155
+ "upstream diagnostic output exceeded its limit",
156
+ pre_execution=False,
157
+ )
158
+ self._digest.update(encoded)
159
+ return len(value)
160
+
161
+ @property
162
+ def hexdigest(self) -> str:
163
+ return self._digest.hexdigest()
164
+
165
+
166
+ class _FrozenResponse:
167
+ def __init__(self, payload: Mapping[str, Any]) -> None:
168
+ self._payload = copy.deepcopy(dict(payload))
169
+ self.ok = True
170
+ self.status_code = 200
171
+
172
+ def json(self) -> Mapping[str, Any]:
173
+ return copy.deepcopy(self._payload)
174
+
175
+
176
+ class _FrozenBindings:
177
+ def __init__(self, value: Mapping[str, Any], corpus_revision: str) -> None:
178
+ self.corpus_revision = corpus_revision
179
+ self.expected_question = _required_text(value, "question", "fixture bindings")
180
+ raw_generations = _mapping_sequence(
181
+ value.get("generator_outputs"),
182
+ "fixture generator_outputs",
183
+ )
184
+ self.generations = [dict(item) for item in raw_generations]
185
+ raw_rounds = _mapping_sequence(value.get("retrieval_rounds"), "fixture retrieval_rounds")
186
+ self.rounds = [dict(item) for item in raw_rounds]
187
+ self.generation_index = 0
188
+ self.retrieval_index = 0
189
+ self.input_tokens = 0
190
+ self.output_tokens = 0
191
+
192
+ def generator_class(self) -> type[Any]:
193
+ owner = self
194
+
195
+ class FrozenGenerator:
196
+ def __init__(self, **kwargs: Any) -> None:
197
+ self.kwargs = dict(kwargs)
198
+
199
+ def generate_text_sequence(self, prompt: str) -> list[tuple[str, float]]:
200
+ if owner.generation_index >= len(owner.generations):
201
+ raise DriverError(
202
+ "FIXTURE_EXHAUSTED",
203
+ "frozen generator output sequence is exhausted",
204
+ pre_execution=False,
205
+ )
206
+ item = owner.generations[owner.generation_index]
207
+ owner.generation_index += 1
208
+ text = _required_text(item, "text", "fixture generation")
209
+ owner.input_tokens += _nonnegative_integer(
210
+ item.get("input_tokens"),
211
+ "fixture input_tokens",
212
+ )
213
+ owner.output_tokens += _nonnegative_integer(
214
+ item.get("output_tokens"),
215
+ "fixture output_tokens",
216
+ )
217
+ if not prompt.strip():
218
+ raise DriverError(
219
+ "FIXTURE_PROMPT_INVALID",
220
+ "upstream generator received an empty prompt",
221
+ pre_execution=False,
222
+ )
223
+ return [(text, 0.0)]
224
+
225
+ return FrozenGenerator
226
+
227
+ def safe_post_request(self, url: str, params: Mapping[str, Any]) -> _FrozenResponse:
228
+ del url
229
+ if self.retrieval_index >= len(self.rounds):
230
+ raise DriverError(
231
+ "FIXTURE_EXHAUSTED",
232
+ "frozen retrieval sequence is exhausted",
233
+ pre_execution=False,
234
+ )
235
+ item = self.rounds[self.retrieval_index]
236
+ self.retrieval_index += 1
237
+ expected_query = _required_text(item, "query", "fixture retrieval")
238
+ actual_query = str(params.get("query_text", "")).strip()
239
+ if actual_query != expected_query:
240
+ raise DriverError(
241
+ "FIXTURE_QUERY_MISMATCH",
242
+ "upstream retrieval query differs from frozen trajectory",
243
+ pre_execution=False,
244
+ )
245
+ documents = _mapping_sequence(item.get("documents"), "fixture documents")
246
+ retrieval = []
247
+ for document in documents:
248
+ retrieval.append(
249
+ {
250
+ **dict(document),
251
+ "corpus_name": self.corpus_revision,
252
+ }
253
+ )
254
+ return _FrozenResponse({"retrieval": retrieval})
255
+
256
+
257
+ class _RetrievalCapture:
258
+ def __init__(self, delegate: Any) -> None:
259
+ self.delegate = delegate
260
+ self.rounds: list[Mapping[str, Any]] = []
261
+
262
+ def __call__(self, url: str, params: Mapping[str, Any]) -> Any:
263
+ response = self.delegate(url, params)
264
+ documents: list[Mapping[str, Any]] = []
265
+ try:
266
+ if response.ok:
267
+ payload = response.json()
268
+ raw = payload.get("retrieval", []) if isinstance(payload, Mapping) else []
269
+ documents = [dict(item) for item in raw if isinstance(item, Mapping)]
270
+ except Exception:
271
+ documents = []
272
+ self.rounds.append(
273
+ {
274
+ "query": str(params.get("query_text", "")),
275
+ "documents": documents,
276
+ }
277
+ )
278
+ return response
279
+
280
+
281
+ class _TracingController:
282
+ def __init__(
283
+ self,
284
+ controller: Any,
285
+ role_by_command: Mapping[str, str],
286
+ retrieval_capture: _RetrievalCapture,
287
+ ) -> None:
288
+ self._controller = controller
289
+ self._roles = dict(role_by_command)
290
+ self._capture = retrieval_capture
291
+ self.events: list[ControllerEvent] = []
292
+
293
+ @property
294
+ def start_state(self) -> Any:
295
+ return self._controller.start_state
296
+
297
+ @property
298
+ def end_state(self) -> Any:
299
+ return self._controller.end_state
300
+
301
+ def init_data(self, value: Mapping[str, Any]) -> Any:
302
+ return self._controller.init_data(value)
303
+
304
+ def execute(self, state: Any, debug: bool = False) -> list[Any]:
305
+ before = _snapshot(state.data)
306
+ capture_start = len(self._capture.rounds)
307
+ outputs = self._controller.execute(state, debug=debug)
308
+ if not isinstance(outputs, list) or len(outputs) != 1:
309
+ raise DriverError(
310
+ "UPSTREAM_OUTPUT_INVALID",
311
+ "IRCoT participant must return exactly one state",
312
+ pre_execution=False,
313
+ )
314
+ output = outputs[0]
315
+ command = state.next
316
+ if not isinstance(command, str) or command not in self._roles:
317
+ raise DriverError(
318
+ "UPSTREAM_OUTPUT_INVALID",
319
+ "IRCoT controller emitted an unknown command",
320
+ pre_execution=False,
321
+ )
322
+ self.events.append(
323
+ ControllerEvent(
324
+ command=command,
325
+ role=self._roles[command],
326
+ before=before,
327
+ after=_snapshot(output.data),
328
+ retrieval_rounds=tuple(self._capture.rounds[capture_start:]),
329
+ )
330
+ )
331
+ return outputs
332
+
333
+
334
+ class IRCoTDriver:
335
+ def __init__(
336
+ self,
337
+ *,
338
+ manifest_path: Path = DEFAULT_MANIFEST,
339
+ profile_id: str | None = None,
340
+ environment: Mapping[str, str] | None = None,
341
+ isolated_process: bool = False,
342
+ ) -> None:
343
+ self.manifest_path = Path(manifest_path)
344
+ self.environment = dict(os.environ if environment is None else environment)
345
+ self.profile_id = profile_id or self.environment.get(PROFILE_ENV, DEFAULT_PROFILE)
346
+ self.isolated_process = isolated_process
347
+
348
+ def identity(self) -> Mapping[str, Any]:
349
+ checkout = self._checkout()
350
+ api_status = self.smoke_upstream_api(checkout=checkout)
351
+ return {
352
+ "profile_id": checkout.profile_id,
353
+ "identity_kind": checkout.profile["identity_kind"],
354
+ "upstream_commit": UPSTREAM_COMMIT,
355
+ "manifest_sha256": checkout.manifest_sha256,
356
+ "config_sha256": checkout.profile["config_sha256"],
357
+ "prompt_sha256": checkout.profile["prompt_sha256"],
358
+ "binding_attestation": checkout.profile["binding_attestation"],
359
+ "full_api_status": api_status["full_api_status"],
360
+ }
361
+
362
+ def smoke_upstream_api(self, *, checkout: Checkout | None = None) -> Mapping[str, str]:
363
+ checkout = checkout or self._checkout()
364
+ with _upstream_import_scope(checkout.root, fixture_shims=False):
365
+ try:
366
+ model_search = importlib.import_module("commaqa.inference.model_search")
367
+ except ModuleNotFoundError as exc:
368
+ raise _dependency_error(exc) from exc
369
+ if not isinstance(getattr(model_search, "BestFirstDecomposer", None), type):
370
+ raise DriverError("UPSTREAM_API_MISMATCH", "BestFirstDecomposer is unavailable")
371
+ full_status = "available"
372
+ try:
373
+ with _upstream_import_scope(checkout.root, fixture_shims=False):
374
+ module = importlib.import_module("commaqa.inference.ircot")
375
+ for symbol in (
376
+ "RetrieveAndResetParagraphsParticipant",
377
+ "StepByStepCOTGenParticipant",
378
+ "StepByStepExitControllerParticipant",
379
+ ):
380
+ if not isinstance(getattr(module, symbol, None), type):
381
+ raise DriverError("UPSTREAM_API_MISMATCH", f"{symbol} is unavailable")
382
+ except DriverError:
383
+ raise
384
+ except ModuleNotFoundError as exc:
385
+ name = exc.name if isinstance(exc.name, str) else "optional_dependency"
386
+ full_status = f"dependency_missing:{name}"
387
+ return {
388
+ "core_symbol": "commaqa.inference.model_search.BestFirstDecomposer",
389
+ "full_api_status": full_status,
390
+ }
391
+
392
+ def run(self, value: Mapping[str, Any]) -> Mapping[str, Any]:
393
+ request = _parse_request(value)
394
+ checkout = self._checkout()
395
+ _attest_request(request, checkout, self.environment)
396
+ identity_kind = checkout.profile["identity_kind"]
397
+ if identity_kind == "upstream_native" and not self.isolated_process:
398
+ raise DriverError(
399
+ "ISOLATION_REQUIRED",
400
+ "upstream-native IRCoT must run through the JSONL worker process",
401
+ )
402
+ if identity_kind == "upstream_native":
403
+ raise DriverError(
404
+ "TOKEN_USAGE_OBSERVER_REQUIRED",
405
+ "historical native clients lack observed token usage; use the startup-bound controlled runtime",
406
+ )
407
+ config = _load_config(checkout, self.environment)
408
+ config = _patch_config(config, checkout, request.controls)
409
+ fixture_bindings = (
410
+ _load_fixture_bindings(checkout, request)
411
+ if identity_kind == "controlled_fixture"
412
+ else None
413
+ )
414
+ capture = _BoundedCapture()
415
+ fixture_shims = fixture_bindings is not None
416
+
417
+ with (
418
+ _working_directory(checkout.root),
419
+ _upstream_import_scope(checkout.root, fixture_shims=fixture_shims),
420
+ contextlib.redirect_stdout(capture),
421
+ ):
422
+ try:
423
+ configurable = importlib.import_module("commaqa.inference.configurable_inference")
424
+ ircot_module = importlib.import_module("commaqa.inference.ircot")
425
+ except ModuleNotFoundError as exc:
426
+ raise _dependency_error(exc) from exc
427
+ if fixture_bindings is not None:
428
+ frozen_generator = fixture_bindings.generator_class()
429
+ ircot_module.GPT3Generator = frozen_generator
430
+ ircot_module.LLMClientGenerator = frozen_generator
431
+ retrieval_delegate = fixture_bindings.safe_post_request
432
+ else:
433
+ retrieval_delegate = ircot_module.safe_post_request
434
+ retrieval_capture = _RetrievalCapture(retrieval_delegate)
435
+ ircot_module.safe_post_request = retrieval_capture
436
+ builder = getattr(configurable, "build_decomposer_and_models", None)
437
+ if not callable(builder):
438
+ raise DriverError("UPSTREAM_API_MISMATCH", "upstream builder is unavailable")
439
+ try:
440
+ decomposer, model_map = builder(copy.deepcopy(config))
441
+ except ModuleNotFoundError as exc:
442
+ raise _dependency_error(exc) from exc
443
+ except DriverError:
444
+ raise
445
+ except Exception as exc:
446
+ raise DriverError(
447
+ "UPSTREAM_BOOT_FAILED",
448
+ "upstream IRCoT participant construction failed",
449
+ ) from exc
450
+ if not isinstance(model_map, Mapping) or not hasattr(decomposer, "controller"):
451
+ raise DriverError("UPSTREAM_API_MISMATCH", "upstream builder result is invalid")
452
+ role_by_command = {key: item["name"] for key, item in config["models"].items()}
453
+ tracing = _TracingController(
454
+ decomposer.controller,
455
+ role_by_command,
456
+ retrieval_capture,
457
+ )
458
+ decomposer.controller = tracing
459
+ example = {
460
+ "qid": request.task_id or request.request_id,
461
+ "query": request.question,
462
+ "question": request.question,
463
+ "metadata": {
464
+ "pinned_title": None,
465
+ "pinned_para": None,
466
+ "pin_position": None,
467
+ },
468
+ "titles": [],
469
+ "paras": [],
470
+ }
471
+ try:
472
+ final_state, _ = decomposer.find_answer_decomp(example, debug=False)
473
+ except DriverError:
474
+ raise
475
+ except SystemExit as exc:
476
+ raise DriverError(
477
+ "UPSTREAM_ABORTED",
478
+ "upstream model client aborted execution",
479
+ pre_execution=False,
480
+ ) from exc
481
+ except Exception as exc:
482
+ raise DriverError(
483
+ "UPSTREAM_EXECUTION_FAILED",
484
+ "upstream IRCoT execution failed",
485
+ pre_execution=False,
486
+ ) from exc
487
+ result = _project_trajectory(
488
+ request,
489
+ checkout,
490
+ config,
491
+ tracing.events,
492
+ final_state,
493
+ ircot_module,
494
+ model_map,
495
+ fixture_bindings,
496
+ )
497
+ result["upstream_stdout_sha256"] = capture.hexdigest
498
+ return result
499
+
500
+ def safe_call(self, value: Mapping[str, Any]) -> Mapping[str, Any]:
501
+ request_id = value.get("request_id", "") if isinstance(value, Mapping) else ""
502
+ try:
503
+ return self.run(value)
504
+ except DriverError as exc:
505
+ response: dict[str, Any] = {
506
+ "status": "error",
507
+ "request_id": request_id if isinstance(request_id, str) else "",
508
+ "failure_code": exc.code,
509
+ "message": exc.public_message,
510
+ "protocol_version": PROTOCOL_VERSION,
511
+ }
512
+ if exc.pre_execution:
513
+ response["consumed"] = False
514
+ return response
515
+
516
+ def _checkout(self) -> Checkout:
517
+ root_value = self.environment.get(UPSTREAM_ENV, "")
518
+ if not root_value:
519
+ raise DriverError("UPSTREAM_ROOT_MISSING", f"{UPSTREAM_ENV} is required")
520
+ root = Path(root_value).expanduser().resolve()
521
+ if not root.is_dir():
522
+ raise DriverError("UPSTREAM_ROOT_INVALID", "IRCoT checkout is unavailable")
523
+ manifest, manifest_sha = _load_manifest(self.manifest_path)
524
+ _verify_checkout(root, manifest)
525
+ profiles = manifest["profiles"]
526
+ profile = profiles.get(self.profile_id) if isinstance(profiles, Mapping) else None
527
+ if not isinstance(profile, Mapping):
528
+ raise DriverError("PROFILE_NOT_ALLOWED", "IRCoT worker profile is not allowlisted")
529
+ if profile.get("identity_kind") not in {"controlled_fixture", "upstream_native"}:
530
+ raise DriverError("MANIFEST_INVALID", "IRCoT profile identity kind is invalid")
531
+ worker_root = self.manifest_path.resolve().parent
532
+ config_path = _profile_file(root, worker_root, profile, "config")
533
+ prompt_path = _profile_file(root, worker_root, profile, "prompt")
534
+ bindings_path = (
535
+ _profile_file(root, worker_root, profile, "bindings")
536
+ if "bindings_path" in profile
537
+ else None
538
+ )
539
+ return Checkout(
540
+ root=root,
541
+ worker_root=worker_root,
542
+ manifest=manifest,
543
+ manifest_sha256=manifest_sha,
544
+ profile_id=self.profile_id,
545
+ profile=profile,
546
+ config_path=config_path,
547
+ prompt_path=prompt_path,
548
+ bindings_path=bindings_path,
549
+ )
550
+
551
+
552
+ def _parse_request(value: Mapping[str, Any]) -> Request:
553
+ if not isinstance(value, Mapping) or set(value) != _REQUEST_FIELDS:
554
+ raise DriverError("INVALID_REQUEST", "IRCoT request fields do not match schema")
555
+ if value.get("upstream_commit") != UPSTREAM_COMMIT:
556
+ raise DriverError("REVISION_MISMATCH", "IRCoT request revision is not pinned")
557
+ if value.get("protocol_version") != PROTOCOL_VERSION:
558
+ raise DriverError("PROTOCOL_MISMATCH", "IRCoT request protocol is unsupported")
559
+ request_id = _bounded_text(value.get("request_id"), "request_id", 256)
560
+ state_hash = _bounded_text(value.get("state_hash"), "state_hash", 128)
561
+ task_id = value.get("task_id")
562
+ if not isinstance(task_id, str) or len(task_id) > 512:
563
+ raise DriverError("INVALID_REQUEST", "task_id must be bounded text")
564
+ question = _bounded_text(value.get("question"), "question", 200_000)
565
+ controls_value = value.get("controls")
566
+ if not isinstance(controls_value, Mapping) or set(controls_value) != _CONTROL_FIELDS:
567
+ raise DriverError("INVALID_REQUEST", "IRCoT controls do not match schema")
568
+ controls = Controls(
569
+ per_step_top_k=_positive_integer(controls_value["per_step_top_k"], "per_step_top_k", 100),
570
+ max_steps=_positive_integer(controls_value["max_steps"], "max_steps", 128),
571
+ max_total_documents=_positive_integer(
572
+ controls_value["max_total_documents"],
573
+ "max_total_documents",
574
+ 10_000,
575
+ ),
576
+ max_output_tokens=_positive_integer(
577
+ controls_value["max_output_tokens"],
578
+ "max_output_tokens",
579
+ 10_000_000,
580
+ ),
581
+ deduplicate=_boolean(controls_value["deduplicate"], "deduplicate"),
582
+ reset_policy=_enum_text(
583
+ controls_value["reset_policy"],
584
+ "reset_policy",
585
+ {"upstream_native", "never", "replace_each_step"},
586
+ ),
587
+ )
588
+ if not controls.deduplicate:
589
+ raise DriverError(
590
+ "CONTROL_UNSUPPORTED",
591
+ "pinned upstream IRCoT always de-duplicates retrieved paragraphs",
592
+ )
593
+ generator = _canonical_mapping(value.get("generator_fingerprint"), "generator_fingerprint")
594
+ retriever = _canonical_mapping(value.get("retriever_fingerprint"), "retriever_fingerprint")
595
+ return Request(
596
+ request_id=request_id,
597
+ state_hash=state_hash,
598
+ task_id=task_id,
599
+ question=question,
600
+ controls=controls,
601
+ generator_fingerprint=generator,
602
+ retriever_fingerprint=retriever,
603
+ corpus_revision=_bounded_text(value.get("corpus_revision"), "corpus_revision", 256),
604
+ index_revision=_bounded_text(value.get("index_revision"), "index_revision", 256),
605
+ prompt_hash=_bounded_text(value.get("prompt_hash"), "prompt_hash", 128),
606
+ )
607
+
608
+
609
+ def _attest_request(request: Request, checkout: Checkout, environment: Mapping[str, str]) -> None:
610
+ profile = checkout.profile
611
+ if request.prompt_hash != profile["prompt_sha256"]:
612
+ raise DriverError("PROMPT_MISMATCH", "request prompt hash differs from pinned prompt")
613
+ if request.corpus_revision != profile.get("corpus_revision"):
614
+ raise DriverError("CORPUS_MISMATCH", "request corpus revision differs from worker profile")
615
+ expected_index = profile.get("index_revision")
616
+ if expected_index is None:
617
+ env_key = profile.get("index_revision_env")
618
+ expected_index = environment.get(env_key, "") if isinstance(env_key, str) else ""
619
+ if not expected_index or request.index_revision != expected_index:
620
+ raise DriverError("INDEX_MISMATCH", "request index revision is not attested")
621
+ if profile["identity_kind"] == "upstream_native":
622
+ generator_env = profile.get("generator_fingerprint_sha256_env")
623
+ retriever_env = profile.get("retriever_fingerprint_sha256_env")
624
+ expected_generator = (
625
+ environment.get(generator_env, "") if isinstance(generator_env, str) else ""
626
+ )
627
+ expected_retriever = (
628
+ environment.get(retriever_env, "") if isinstance(retriever_env, str) else ""
629
+ )
630
+ if (
631
+ not expected_generator
632
+ or _canonical_hash(request.generator_fingerprint) != expected_generator
633
+ ):
634
+ raise DriverError("GENERATOR_IDENTITY_MISMATCH", "generator binding is not attested")
635
+ if (
636
+ not expected_retriever
637
+ or _canonical_hash(request.retriever_fingerprint) != expected_retriever
638
+ ):
639
+ raise DriverError("RETRIEVER_IDENTITY_MISMATCH", "retriever binding is not attested")
640
+ required = profile.get("required_runtime_env", [])
641
+ if not isinstance(required, Sequence) or isinstance(required, (str, bytes)):
642
+ raise DriverError("MANIFEST_INVALID", "required runtime environment list is invalid")
643
+ if any(not isinstance(key, str) or not environment.get(key, "") for key in required):
644
+ raise DriverError("RUNTIME_BINDING_MISSING", "production model binding is unavailable")
645
+
646
+
647
+ def _load_config(checkout: Checkout, environment: Mapping[str, str]) -> Mapping[str, Any]:
648
+ if checkout.config_path.suffix == ".json":
649
+ try:
650
+ value = json.loads(checkout.config_path.read_text(encoding="utf-8"))
651
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
652
+ raise DriverError("CONFIG_INVALID", "pinned JSON config cannot be loaded") from exc
653
+ elif checkout.config_path.suffix == ".jsonnet":
654
+ try:
655
+ jsonnet = importlib.import_module("_jsonnet")
656
+ except ModuleNotFoundError as exc:
657
+ raise _dependency_error(exc) from exc
658
+ ext_var_spec = checkout.profile.get("config_ext_vars", {})
659
+ if not isinstance(ext_var_spec, Mapping):
660
+ raise DriverError("MANIFEST_INVALID", "config extVar specification is invalid")
661
+ ext_vars: dict[str, str] = {}
662
+ for ext_name, env_name in ext_var_spec.items():
663
+ if not isinstance(ext_name, str) or not isinstance(env_name, str):
664
+ raise DriverError("MANIFEST_INVALID", "config extVar names must be strings")
665
+ env_value = environment.get(env_name, "")
666
+ if not env_value:
667
+ raise DriverError(
668
+ "RUNTIME_BINDING_MISSING", "production retriever binding is unavailable"
669
+ )
670
+ ext_vars[ext_name] = env_value
671
+ try:
672
+ value = json.loads(jsonnet.evaluate_file(str(checkout.config_path), ext_vars=ext_vars))
673
+ except (RuntimeError, json.JSONDecodeError) as exc:
674
+ raise DriverError(
675
+ "CONFIG_INVALID", "pinned Jsonnet config cannot be evaluated"
676
+ ) from exc
677
+ else:
678
+ raise DriverError("CONFIG_INVALID", "pinned config extension is unsupported")
679
+ if not isinstance(value, Mapping):
680
+ raise DriverError("CONFIG_INVALID", "pinned config root must be an object")
681
+ return value
682
+
683
+
684
+ def _patch_config(
685
+ config: Mapping[str, Any],
686
+ checkout: Checkout,
687
+ controls: Controls,
688
+ ) -> Mapping[str, Any]:
689
+ patched = copy.deepcopy(dict(config))
690
+ models = patched.get("models")
691
+ if not isinstance(models, Mapping):
692
+ raise DriverError("CONFIG_INVALID", "IRCoT config requires models")
693
+ allowed = _string_set(checkout.manifest["allowed_model_names"], "allowed model names")
694
+ roles: set[str] = set()
695
+ per_call_output = max(1, controls.max_output_tokens // controls.max_steps)
696
+ for model in models.values():
697
+ if not isinstance(model, dict):
698
+ raise DriverError("CONFIG_INVALID", "IRCoT model config must be an object")
699
+ name = model.get("name")
700
+ if not isinstance(name, str) or name not in allowed:
701
+ raise DriverError("UNSAFE_CONFIG", "IRCoT config contains a non-allowlisted model")
702
+ roles.add(name)
703
+ if name == "retrieve_and_reset_paragraphs":
704
+ model["retrieval_count"] = controls.per_step_top_k
705
+ model["global_max_num_paras"] = controls.max_total_documents
706
+ model["cumulate_titles"] = controls.reset_policy != "replace_each_step"
707
+ elif name == "step_by_step_cot_gen":
708
+ model["max_num_sentences"] = controls.max_steps
709
+ if "max_tokens" in model or model.get("gen_model") == "gpt3":
710
+ model["max_tokens"] = min(
711
+ int(model.get("max_tokens", per_call_output)), per_call_output
712
+ )
713
+ if "max_length" in model or model.get("gen_model") == "llm_api":
714
+ model["max_length"] = min(
715
+ int(model.get("max_length", per_call_output)), per_call_output
716
+ )
717
+ prompt_file = model.get("prompt_file")
718
+ if checkout.profile["identity_kind"] == "controlled_fixture":
719
+ if prompt_file != "__AGENTINPUTLAB_LOCKED_PROMPT__":
720
+ raise DriverError("CONFIG_INVALID", "fixture prompt marker is missing")
721
+ model["prompt_file"] = str(checkout.prompt_path)
722
+ else:
723
+ resolved = _safe_file(checkout.root, prompt_file)
724
+ if resolved != checkout.prompt_path:
725
+ raise DriverError("PROMPT_MISMATCH", "config uses an unpinned prompt")
726
+ elif name == "step_by_step_exit_controller":
727
+ model["max_num_sentences"] = controls.max_steps
728
+ model["global_max_num_paras"] = controls.max_total_documents
729
+ required_roles = {
730
+ "retrieve_and_reset_paragraphs",
731
+ "step_by_step_cot_gen",
732
+ "step_by_step_exit_controller",
733
+ }
734
+ if roles != required_roles:
735
+ raise DriverError(
736
+ "CONFIG_INVALID", "IRCoT profile must contain the complete three-stage loop"
737
+ )
738
+ return patched
739
+
740
+
741
+ def _project_trajectory(
742
+ request: Request,
743
+ checkout: Checkout,
744
+ config: Mapping[str, Any],
745
+ events: Sequence[ControllerEvent],
746
+ final_state: Any,
747
+ ircot_module: Any,
748
+ model_map: Mapping[str, Any],
749
+ fixture_bindings: _FrozenBindings | None,
750
+ ) -> dict[str, Any]:
751
+ retrieval_events = [event for event in events if event.role == "retrieve_and_reset_paragraphs"]
752
+ if not retrieval_events:
753
+ raise DriverError(
754
+ "UPSTREAM_OUTPUT_INVALID",
755
+ "upstream IRCoT trajectory contains no retrieval",
756
+ pre_execution=False,
757
+ )
758
+ if len(retrieval_events) - 1 > request.controls.max_steps:
759
+ raise DriverError(
760
+ "UPSTREAM_OUTPUT_INVALID",
761
+ "upstream IRCoT trajectory exceeds max_steps",
762
+ pre_execution=False,
763
+ )
764
+ document_records: dict[str, dict[str, Any]] = {}
765
+ pair_to_id: dict[tuple[str, str], str] = {}
766
+ seen: set[str] = set()
767
+ steps: list[dict[str, Any]] = []
768
+ active_ids: tuple[str, ...] = ()
769
+ initial_ids: tuple[str, ...] = ()
770
+
771
+ for retrieval_index, event in enumerate(retrieval_events):
772
+ raw_hits = [
773
+ hit
774
+ for round_value in event.retrieval_rounds
775
+ for hit in _mapping_sequence(round_value.get("documents", []), "retrieval documents")
776
+ ]
777
+ hit_by_pair: dict[tuple[str, str], Mapping[str, Any]] = {}
778
+ for hit in raw_hits:
779
+ title = str(hit.get("title", ""))
780
+ paragraph = str(hit.get("paragraph_text", ""))
781
+ if title and paragraph:
782
+ hit_by_pair[(title, paragraph)] = hit
783
+ current_ids: list[str] = []
784
+ for title, paragraph in zip(
785
+ event.after.titles,
786
+ event.after.paragraphs,
787
+ strict=True,
788
+ ):
789
+ pair = (title, paragraph)
790
+ hit = hit_by_pair.get(pair, {})
791
+ document_id = pair_to_id.get(pair)
792
+ if document_id is None:
793
+ raw_id = hit.get("id") if isinstance(hit, Mapping) else None
794
+ document_id = (
795
+ raw_id
796
+ if isinstance(raw_id, str) and raw_id
797
+ else str(ircot_module.get_pid_for_title_paragraph_text(title, paragraph))
798
+ )
799
+ pair_to_id[pair] = document_id
800
+ score_value = hit.get("score") if isinstance(hit, Mapping) else None
801
+ score = _optional_finite_number(score_value, "document score")
802
+ if document_id not in document_records or hit:
803
+ document_records[document_id] = {
804
+ "document_id": document_id,
805
+ "text": paragraph,
806
+ "title": title,
807
+ "corpus_revision": request.corpus_revision,
808
+ "score": score,
809
+ "metadata": {
810
+ "worker_profile": checkout.profile_id,
811
+ "worker_identity_kind": checkout.profile["identity_kind"],
812
+ },
813
+ }
814
+ current_ids.append(document_id)
815
+ active_ids = tuple(dict.fromkeys(current_ids))
816
+ raw_retrieved: list[str] = []
817
+ for pair in hit_by_pair:
818
+ if pair in pair_to_id and pair_to_id[pair] in active_ids:
819
+ raw_retrieved.append(pair_to_id[pair])
820
+ before_pairs = set(zip(event.before.titles, event.before.paragraphs, strict=True))
821
+ new_active = [
822
+ pair_to_id[pair]
823
+ for pair in pair_to_id
824
+ if pair not in before_pairs and pair_to_id[pair] in active_ids
825
+ ]
826
+ for document_id in new_active:
827
+ if document_id not in raw_retrieved:
828
+ raw_retrieved.append(document_id)
829
+ retrieved_ids = tuple(dict.fromkeys(raw_retrieved))
830
+ if retrieval_index == 0:
831
+ initial_ids = active_ids
832
+ seen.update(initial_ids)
833
+ continue
834
+ generated = tuple(
835
+ str(item)
836
+ for item in ircot_module.remove_reasoning_sentences(
837
+ list(event.before.generated_sentences)
838
+ )
839
+ if str(item).strip()
840
+ )
841
+ latest = generated[-1].strip() if generated else request.question.strip()
842
+ cot_step = " ".join(generated).strip() or latest
843
+ new_ids = tuple(document_id for document_id in retrieved_ids if document_id not in seen)
844
+ seen.update(new_ids)
845
+ reset_applied = request.controls.reset_policy == "replace_each_step"
846
+ steps.append(
847
+ {
848
+ "index": len(steps),
849
+ "cot_step": cot_step,
850
+ "latest_cot_sentence": latest,
851
+ "retrieval_query": latest,
852
+ "retrieved_document_ids": list(retrieved_ids),
853
+ "new_document_ids": list(new_ids),
854
+ "active_document_ids": list(active_ids),
855
+ "reset_applied": reset_applied,
856
+ }
857
+ )
858
+ final_generated = (
859
+ _snapshot(final_state.data).generated_sentences if final_state is not None else ()
860
+ )
861
+ exit_model = next(
862
+ model
863
+ for model in config["models"].values()
864
+ if model["name"] == "step_by_step_exit_controller"
865
+ )
866
+ regex = re.compile(str(exit_model.get("answer_extractor_regex", ".* answer is:? (.*)")))
867
+ answer = ""
868
+ if final_generated:
869
+ match = regex.match(final_generated[-1])
870
+ if match:
871
+ answer = match.group(1)
872
+ if exit_model.get("answer_extractor_remove_last_fullstop") and answer.endswith("."):
873
+ answer = answer[:-1]
874
+ if answer:
875
+ exit_reason = "answer_detected"
876
+ elif final_state is None:
877
+ exit_reason = "upstream"
878
+ elif len(steps) == request.controls.max_steps:
879
+ exit_reason = "max_steps"
880
+ else:
881
+ exit_reason = "controller"
882
+ model_calls = _model_calls(model_map)
883
+ if model_calls < 1:
884
+ raise DriverError(
885
+ "USAGE_ACCOUNTING_UNAVAILABLE",
886
+ "upstream IRCoT reported no model calls",
887
+ pre_execution=False,
888
+ )
889
+ input_tokens = fixture_bindings.input_tokens if fixture_bindings is not None else 0
890
+ output_tokens = fixture_bindings.output_tokens if fixture_bindings is not None else 0
891
+ if output_tokens > request.controls.max_output_tokens:
892
+ raise DriverError(
893
+ "OUTPUT_BUDGET_EXCEEDED",
894
+ "upstream IRCoT exceeded max_output_tokens",
895
+ pre_execution=False,
896
+ )
897
+ return {
898
+ "status": "ok",
899
+ "request_id": request.request_id,
900
+ "protocol_version": PROTOCOL_VERSION,
901
+ "upstream_commit": UPSTREAM_COMMIT,
902
+ "initial_query": request.question,
903
+ "initial_document_ids": list(initial_ids),
904
+ "steps": steps,
905
+ "documents": list(document_records.values()),
906
+ "active_document_ids": list(active_ids),
907
+ "exit_reason": exit_reason,
908
+ "answer": answer,
909
+ "usage": {
910
+ "retrieval_calls": 1 + len(steps),
911
+ "model_calls": model_calls,
912
+ "unpriced_model_calls": model_calls,
913
+ "input_tokens": input_tokens,
914
+ "output_tokens": output_tokens,
915
+ },
916
+ "worker_profile_id": checkout.profile_id,
917
+ "worker_identity_kind": checkout.profile["identity_kind"],
918
+ "binding_attestation": checkout.profile["binding_attestation"],
919
+ }
920
+
921
+
922
+ def _model_calls(model_map: Mapping[str, Any]) -> int:
923
+ total = 0
924
+ for participant in model_map.values():
925
+ reporter = getattr(participant, "return_model_calls", None)
926
+ if not callable(reporter):
927
+ continue
928
+ counters = reporter()
929
+ if not isinstance(counters, Mapping):
930
+ raise DriverError("USAGE_ACCOUNTING_UNAVAILABLE", "participant counters are invalid")
931
+ for name, amount in counters.items():
932
+ if isinstance(amount, bool) or not isinstance(amount, int) or amount < 0:
933
+ raise DriverError("USAGE_ACCOUNTING_UNAVAILABLE", "participant counter is invalid")
934
+ if name in {"step_by_step_cot", "llm_qa"}:
935
+ total += amount
936
+ return total
937
+
938
+
939
+ def _snapshot(data: Any) -> Snapshot:
940
+ try:
941
+ titles = tuple(str(item) for item in data.get("titles", []))
942
+ paragraphs = tuple(str(item) for item in data.get("paras", []))
943
+ generated = tuple(str(item) for item in data.get("generated_sentences", []))
944
+ question = str(data.get("question", ""))
945
+ except (AttributeError, TypeError, ValueError) as exc:
946
+ raise DriverError(
947
+ "UPSTREAM_OUTPUT_INVALID",
948
+ "upstream IRCoT state cannot be inspected",
949
+ pre_execution=False,
950
+ ) from exc
951
+ if len(titles) != len(paragraphs):
952
+ raise DriverError(
953
+ "UPSTREAM_OUTPUT_INVALID",
954
+ "upstream titles and paragraphs are unbalanced",
955
+ pre_execution=False,
956
+ )
957
+ return Snapshot(question, titles, paragraphs, generated)
958
+
959
+
960
+ def _load_fixture_bindings(checkout: Checkout, request: Request) -> _FrozenBindings:
961
+ if checkout.bindings_path is None:
962
+ raise DriverError("MANIFEST_INVALID", "fixture profile lacks frozen bindings")
963
+ try:
964
+ value = json.loads(checkout.bindings_path.read_text(encoding="utf-8"))
965
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
966
+ raise DriverError("FIXTURE_INVALID", "frozen bindings cannot be loaded") from exc
967
+ if not isinstance(value, Mapping):
968
+ raise DriverError("FIXTURE_INVALID", "frozen bindings root must be an object")
969
+ bindings = _FrozenBindings(value, request.corpus_revision)
970
+ if request.question != bindings.expected_question:
971
+ raise DriverError("FIXTURE_QUESTION_MISMATCH", "question is outside frozen fixture")
972
+ return bindings
973
+
974
+
975
+ def _load_manifest(path: Path) -> tuple[Mapping[str, Any], str]:
976
+ try:
977
+ raw = path.read_bytes()
978
+ value = json.loads(raw)
979
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
980
+ raise DriverError("MANIFEST_INVALID", "IRCoT manifest cannot be loaded") from exc
981
+ if not isinstance(value, Mapping) or value.get("schema_version") != MANIFEST_SCHEMA:
982
+ raise DriverError("MANIFEST_INVALID", "IRCoT manifest schema is invalid")
983
+ upstream = value.get("upstream")
984
+ if not isinstance(upstream, Mapping) or upstream.get("commit") != UPSTREAM_COMMIT:
985
+ raise DriverError("MANIFEST_INVALID", "IRCoT manifest revision is invalid")
986
+ for key in ("source_files", "allowed_model_names", "profiles", "patches"):
987
+ if key not in value:
988
+ raise DriverError("MANIFEST_INVALID", f"IRCoT manifest lacks {key}")
989
+ return value, _sha256_bytes(raw)
990
+
991
+
992
+ def _verify_checkout(root: Path, manifest: Mapping[str, Any]) -> None:
993
+ if _read_git_head(root) != UPSTREAM_COMMIT:
994
+ raise DriverError("REVISION_MISMATCH", "IRCoT checkout is at the wrong commit")
995
+ upstream = manifest["upstream"]
996
+ if upstream.get("license_spdx") != "Apache-2.0":
997
+ raise DriverError("MANIFEST_INVALID", "IRCoT license declaration is invalid")
998
+ if _sha256_file(root / "LICENSE") != upstream.get("license_sha256"):
999
+ raise DriverError("LICENSE_MISMATCH", "IRCoT Apache-2.0 license does not match pin")
1000
+ sources = manifest["source_files"]
1001
+ if not isinstance(sources, Mapping) or not sources:
1002
+ raise DriverError("MANIFEST_INVALID", "IRCoT source pins are invalid")
1003
+ for relative, expected in sources.items():
1004
+ if not isinstance(expected, str) or _sha256_file(_safe_file(root, relative)) != expected:
1005
+ raise DriverError("SOURCE_MISMATCH", "IRCoT source does not match manifest")
1006
+ if upstream.get("require_clean_checkout") is True:
1007
+ _require_clean_checkout(root)
1008
+
1009
+
1010
+ def _profile_file(
1011
+ upstream_root: Path,
1012
+ worker_root: Path,
1013
+ profile: Mapping[str, Any],
1014
+ prefix: str,
1015
+ ) -> Path:
1016
+ scope = profile.get(f"{prefix}_scope")
1017
+ relative = profile.get(f"{prefix}_path")
1018
+ expected = profile.get(f"{prefix}_sha256")
1019
+ root = upstream_root if scope == "upstream" else worker_root if scope == "worker" else None
1020
+ if root is None or not isinstance(expected, str):
1021
+ raise DriverError("MANIFEST_INVALID", f"{prefix} artifact declaration is invalid")
1022
+ path = _safe_file(root, relative)
1023
+ if _sha256_file(path) != expected:
1024
+ raise DriverError(f"{prefix.upper()}_MISMATCH", f"pinned {prefix} checksum differs")
1025
+ return path
1026
+
1027
+
1028
+ @contextlib.contextmanager
1029
+ def _working_directory(path: Path) -> Iterator[None]:
1030
+ previous = Path.cwd()
1031
+ os.chdir(path)
1032
+ try:
1033
+ yield
1034
+ finally:
1035
+ os.chdir(previous)
1036
+
1037
+
1038
+ @contextlib.contextmanager
1039
+ def _upstream_import_scope(root: Path, *, fixture_shims: bool) -> Iterator[None]:
1040
+ existing = {name for name in sys.modules if name == "commaqa" or name.startswith("commaqa.")}
1041
+ if existing:
1042
+ raise DriverError("IMPORT_STATE_UNSAFE", "commaqa modules were already loaded")
1043
+ shims = _fixture_shims() if fixture_shims else {}
1044
+ prior_modules = {name: sys.modules.get(name) for name in shims}
1045
+ for name, module in shims.items():
1046
+ sys.modules[name] = module
1047
+ sys.path.insert(0, str(root))
1048
+ importlib.invalidate_caches()
1049
+ try:
1050
+ yield
1051
+ finally:
1052
+ sys.path.remove(str(root))
1053
+ for name in tuple(sys.modules):
1054
+ if name == "commaqa" or name.startswith("commaqa."):
1055
+ del sys.modules[name]
1056
+ for name, previous in prior_modules.items():
1057
+ if previous is None:
1058
+ sys.modules.pop(name, None)
1059
+ else:
1060
+ sys.modules[name] = previous
1061
+ importlib.invalidate_caches()
1062
+
1063
+
1064
+ def _fixture_shims() -> Mapping[str, types.ModuleType]:
1065
+ diskcache = types.ModuleType("diskcache")
1066
+
1067
+ class Cache:
1068
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
1069
+ del args, kwargs
1070
+
1071
+ def memoize(self, *args: Any, **kwargs: Any) -> Any:
1072
+ del args, kwargs
1073
+
1074
+ def decorate(function: Any) -> Any:
1075
+ return function
1076
+
1077
+ return decorate
1078
+
1079
+ diskcache.Cache = Cache
1080
+ rapidfuzz = types.ModuleType("rapidfuzz")
1081
+ fuzz = types.ModuleType("rapidfuzz.fuzz")
1082
+
1083
+ def ratio(left: str, right: str) -> float:
1084
+ return 100.0 * difflib.SequenceMatcher(None, left, right).ratio()
1085
+
1086
+ fuzz.ratio = ratio
1087
+ fuzz.token_sort_ratio = lambda left, right: ratio(
1088
+ " ".join(sorted(left.split())),
1089
+ " ".join(sorted(right.split())),
1090
+ )
1091
+ rapidfuzz.fuzz = fuzz
1092
+ ftfy = types.ModuleType("ftfy")
1093
+ ftfy.fix_text = lambda value: value
1094
+ dateutil = types.ModuleType("dateutil")
1095
+ dateutil_parser = types.ModuleType("dateutil.parser")
1096
+
1097
+ def unsupported_date_parse(*args: Any, **kwargs: Any) -> Any:
1098
+ del args, kwargs
1099
+ raise ValueError("date parsing is unavailable in the frozen IRCoT fixture")
1100
+
1101
+ dateutil_parser.parse = unsupported_date_parse
1102
+ dateutil.parser = dateutil_parser
1103
+ jsonnet = types.ModuleType("_jsonnet")
1104
+ jsonnet.evaluate_file = lambda *args, **kwargs: (_ for _ in ()).throw(
1105
+ RuntimeError("Jsonnet is unavailable in the frozen fixture")
1106
+ )
1107
+ nltk = types.ModuleType("nltk")
1108
+ nltk_corpus = types.ModuleType("nltk.corpus")
1109
+ nltk_stem = types.ModuleType("nltk.stem")
1110
+ nltk_porter = types.ModuleType("nltk.stem.porter")
1111
+
1112
+ class Stopwords:
1113
+ @staticmethod
1114
+ def words(language: str) -> list[str]:
1115
+ del language
1116
+ return []
1117
+
1118
+ class PorterStemmer:
1119
+ @staticmethod
1120
+ def stem(token: str) -> str:
1121
+ return token
1122
+
1123
+ nltk.word_tokenize = lambda value: value.split()
1124
+ nltk_corpus.stopwords = Stopwords()
1125
+ nltk_porter.PorterStemmer = PorterStemmer
1126
+ nltk.corpus = nltk_corpus
1127
+ nltk.stem = nltk_stem
1128
+ nltk_stem.porter = nltk_porter
1129
+ spacy = types.ModuleType("spacy")
1130
+
1131
+ class Sentence:
1132
+ def __init__(self, text: str) -> None:
1133
+ self.text = text
1134
+ self.text_with_ws = text
1135
+
1136
+ class Document:
1137
+ def __init__(self, text: str) -> None:
1138
+ self.sents = [Sentence(text)] if text else []
1139
+
1140
+ spacy.load = lambda name: lambda text: Document(text)
1141
+ return {
1142
+ "diskcache": diskcache,
1143
+ "rapidfuzz": rapidfuzz,
1144
+ "rapidfuzz.fuzz": fuzz,
1145
+ "ftfy": ftfy,
1146
+ "dateutil": dateutil,
1147
+ "dateutil.parser": dateutil_parser,
1148
+ "_jsonnet": jsonnet,
1149
+ "nltk": nltk,
1150
+ "nltk.corpus": nltk_corpus,
1151
+ "nltk.stem": nltk_stem,
1152
+ "nltk.stem.porter": nltk_porter,
1153
+ "spacy": spacy,
1154
+ }
1155
+
1156
+
1157
+ def _read_git_head(root: Path) -> str:
1158
+ try:
1159
+ head = (root / ".git" / "HEAD").read_text(encoding="utf-8").strip()
1160
+ if head.startswith("ref: "):
1161
+ head = (
1162
+ _safe_file(root / ".git", head.removeprefix("ref: "))
1163
+ .read_text(encoding="utf-8")
1164
+ .strip()
1165
+ )
1166
+ except (OSError, UnicodeDecodeError) as exc:
1167
+ raise DriverError("REVISION_MISMATCH", "IRCoT checkout HEAD is unavailable") from exc
1168
+ if len(head) != 40 or any(character not in "0123456789abcdef" for character in head):
1169
+ raise DriverError("REVISION_MISMATCH", "IRCoT checkout HEAD is invalid")
1170
+ return head
1171
+
1172
+
1173
+ def _require_clean_checkout(root: Path) -> None:
1174
+ try:
1175
+ result = subprocess.run(
1176
+ (
1177
+ "git",
1178
+ "-C",
1179
+ str(root),
1180
+ "status",
1181
+ "--porcelain=v1",
1182
+ "--untracked-files=all",
1183
+ ),
1184
+ check=False,
1185
+ capture_output=True,
1186
+ text=True,
1187
+ timeout=10,
1188
+ env={"PATH": os.environ.get("PATH", "")},
1189
+ )
1190
+ except (OSError, subprocess.TimeoutExpired) as exc:
1191
+ raise DriverError("GIT_VERIFICATION_FAILED", "IRCoT git check is unavailable") from exc
1192
+ if result.returncode != 0:
1193
+ raise DriverError("GIT_VERIFICATION_FAILED", "IRCoT git check failed")
1194
+ if result.stdout.strip():
1195
+ raise DriverError("DIRTY_CHECKOUT", "IRCoT checkout contains uncommitted files")
1196
+
1197
+
1198
+ def _safe_file(root: Path, relative: Any) -> Path:
1199
+ if not isinstance(relative, str) or not relative or "\\" in relative or "\x00" in relative:
1200
+ raise DriverError("PATH_INVALID", "manifest path is invalid")
1201
+ candidate = (root.resolve() / relative).resolve()
1202
+ try:
1203
+ candidate.relative_to(root.resolve())
1204
+ except ValueError as exc:
1205
+ raise DriverError("PATH_INVALID", "manifest path escapes its artifact root") from exc
1206
+ if not candidate.is_file():
1207
+ raise DriverError("PATH_INVALID", "manifest path is not a file")
1208
+ return candidate
1209
+
1210
+
1211
+ def _canonical_mapping(value: Any, label: str) -> Mapping[str, Any]:
1212
+ if not isinstance(value, Mapping):
1213
+ raise DriverError("INVALID_REQUEST", f"{label} must be a mapping")
1214
+ _reject_secret_fields(value)
1215
+ try:
1216
+ encoded = _canonical_json(value)
1217
+ except (TypeError, ValueError) as exc:
1218
+ raise DriverError("INVALID_REQUEST", f"{label} must be canonical JSON") from exc
1219
+ if len(encoded.encode("utf-8")) > 128 * 1024:
1220
+ raise DriverError("INVALID_REQUEST", f"{label} exceeds size limit")
1221
+ return json.loads(encoded)
1222
+
1223
+
1224
+ def _reject_secret_fields(value: Mapping[str, Any]) -> None:
1225
+ pending: list[Any] = [value]
1226
+ while pending:
1227
+ current = pending.pop()
1228
+ if isinstance(current, Mapping):
1229
+ for key, item in current.items():
1230
+ if not isinstance(key, str):
1231
+ raise DriverError("INVALID_REQUEST", "fingerprint keys must be strings")
1232
+ normalized = key.lower().replace("-", "_")
1233
+ if (
1234
+ any(part in normalized for part in _SECRET_KEY_PARTS)
1235
+ or normalized in _SECRET_TOKEN_KEYS
1236
+ ):
1237
+ raise DriverError("SECRET_REJECTED", "fingerprints must not contain secrets")
1238
+ pending.append(item)
1239
+ elif isinstance(current, Sequence) and not isinstance(current, (str, bytes, bytearray)):
1240
+ pending.extend(current)
1241
+
1242
+
1243
+ def _mapping_sequence(value: Any, label: str) -> tuple[Mapping[str, Any], ...]:
1244
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
1245
+ raise DriverError("UPSTREAM_OUTPUT_INVALID", f"{label} must be a sequence")
1246
+ result = tuple(value)
1247
+ if not all(isinstance(item, Mapping) for item in result):
1248
+ raise DriverError("UPSTREAM_OUTPUT_INVALID", f"{label} must contain mappings")
1249
+ return result # type: ignore[return-value]
1250
+
1251
+
1252
+ def _required_text(value: Mapping[str, Any], key: str, label: str) -> str:
1253
+ item = value.get(key)
1254
+ if not isinstance(item, str) or not item.strip():
1255
+ raise DriverError("FIXTURE_INVALID", f"{label}.{key} must be text")
1256
+ return item
1257
+
1258
+
1259
+ def _bounded_text(value: Any, label: str, limit: int) -> str:
1260
+ if not isinstance(value, str) or not value.strip() or len(value) > limit or "\x00" in value:
1261
+ raise DriverError("INVALID_REQUEST", f"{label} must be bounded non-empty text")
1262
+ return value
1263
+
1264
+
1265
+ def _positive_integer(value: Any, label: str, maximum: int) -> int:
1266
+ if isinstance(value, bool) or not isinstance(value, int) or not 1 <= value <= maximum:
1267
+ raise DriverError("INVALID_REQUEST", f"{label} must be a bounded positive integer")
1268
+ return value
1269
+
1270
+
1271
+ def _nonnegative_integer(value: Any, label: str) -> int:
1272
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
1273
+ raise DriverError("FIXTURE_INVALID", f"{label} must be non-negative")
1274
+ return value
1275
+
1276
+
1277
+ def _optional_finite_number(value: Any, label: str) -> float | None:
1278
+ if value is None:
1279
+ return None
1280
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
1281
+ raise DriverError("UPSTREAM_OUTPUT_INVALID", f"{label} must be numeric")
1282
+ result = float(value)
1283
+ if not math.isfinite(result):
1284
+ raise DriverError("UPSTREAM_OUTPUT_INVALID", f"{label} must be finite")
1285
+ return result
1286
+
1287
+
1288
+ def _boolean(value: Any, label: str) -> bool:
1289
+ if not isinstance(value, bool):
1290
+ raise DriverError("INVALID_REQUEST", f"{label} must be boolean")
1291
+ return value
1292
+
1293
+
1294
+ def _enum_text(value: Any, label: str, allowed: set[str]) -> str:
1295
+ if not isinstance(value, str) or value not in allowed:
1296
+ raise DriverError("INVALID_REQUEST", f"{label} is unsupported")
1297
+ return value
1298
+
1299
+
1300
+ def _string_set(value: Any, label: str) -> frozenset[str]:
1301
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
1302
+ raise DriverError("MANIFEST_INVALID", f"{label} must be a sequence")
1303
+ result = tuple(value)
1304
+ if not all(isinstance(item, str) and item for item in result):
1305
+ raise DriverError("MANIFEST_INVALID", f"{label} must contain strings")
1306
+ return frozenset(result)
1307
+
1308
+
1309
+ def _dependency_error(error: ModuleNotFoundError) -> DriverError:
1310
+ name = error.name if isinstance(error.name, str) else "optional_dependency"
1311
+ return DriverError("DEPENDENCY_MISSING", f"required upstream dependency is missing: {name}")
1312
+
1313
+
1314
+ def _canonical_hash(value: Any) -> str:
1315
+ return _sha256_bytes(_canonical_json(value).encode("utf-8"))
1316
+
1317
+
1318
+ def _canonical_json(value: Any) -> str:
1319
+ return json.dumps(
1320
+ value,
1321
+ ensure_ascii=False,
1322
+ sort_keys=True,
1323
+ separators=(",", ":"),
1324
+ allow_nan=False,
1325
+ )
1326
+
1327
+
1328
+ def _sha256_file(path: Path) -> str:
1329
+ try:
1330
+ return _sha256_bytes(path.read_bytes())
1331
+ except OSError as exc:
1332
+ raise DriverError("ARTIFACT_MISSING", "pinned IRCoT artifact is unavailable") from exc
1333
+
1334
+
1335
+ def _sha256_bytes(value: bytes) -> str:
1336
+ return hashlib.sha256(value).hexdigest()
1337
+
1338
+
1339
+ def serve_jsonl(driver: IRCoTDriver, source: TextIO, sink: TextIO) -> int:
1340
+ for line in source:
1341
+ if len(line.encode("utf-8")) > MAX_REQUEST_BYTES:
1342
+ response: Mapping[str, Any] = {
1343
+ "status": "error",
1344
+ "failure_code": "REQUEST_TOO_LARGE",
1345
+ "message": "IRCoT request exceeds byte limit",
1346
+ "protocol_version": PROTOCOL_VERSION,
1347
+ "consumed": False,
1348
+ }
1349
+ else:
1350
+ try:
1351
+ value = json.loads(line)
1352
+ except json.JSONDecodeError:
1353
+ response = {
1354
+ "status": "error",
1355
+ "failure_code": "INVALID_JSON",
1356
+ "message": "IRCoT request is not valid JSON",
1357
+ "protocol_version": PROTOCOL_VERSION,
1358
+ "consumed": False,
1359
+ }
1360
+ else:
1361
+ response = driver.safe_call(value)
1362
+ sink.write(_canonical_json(response) + "\n")
1363
+ sink.flush()
1364
+ return 0
1365
+
1366
+
1367
+ def main() -> int:
1368
+ driver = IRCoTDriver(isolated_process=True)
1369
+ return serve_jsonl(driver, sys.stdin, sys.stdout)
1370
+
1371
+
1372
+ if __name__ == "__main__":
1373
+ raise SystemExit(main())