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,1514 @@
1
+ #!/usr/bin/env python3
2
+ """Pinned, fail-closed JSONL worker for the official LLMCompiler runtime.
3
+
4
+ The worker deliberately supports frozen model and tool replays only. It uses
5
+ the official Planner, TaskFetchingUnit, LLMCompiler join, and replan loop; the
6
+ local code supplies transport verification, bounded tracing, and side-effect
7
+ policy rather than a clean-room implementation of the algorithm.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import asyncio
14
+ import contextlib
15
+ import hashlib
16
+ import importlib
17
+ import importlib.metadata
18
+ import inspect
19
+ import io
20
+ import json
21
+ import math
22
+ import os
23
+ import re
24
+ import subprocess
25
+ import sys
26
+ from collections.abc import Mapping, Sequence
27
+ from dataclasses import dataclass, field
28
+ from pathlib import Path
29
+ from types import ModuleType, SimpleNamespace
30
+ from typing import Any
31
+ from uuid import uuid4
32
+
33
+ UPSTREAM_COMMIT = "a00c9d35507507da70e8c637eee64efc8c1857ae"
34
+ PROTOCOL_VERSION = "agentinputlab.llm-compiler.v1"
35
+ COMPATIBILITY_MANIFEST_SHA256 = "4b22356ddd587b0fac952511ab53dcf1ae0007b9645e02cd6375122a41ddedd6"
36
+ _MANIFEST_PATH = Path(__file__).with_name("compatibility_manifest.json")
37
+ _MAX_MANIFEST_BYTES = 8 * 1024 * 1024
38
+ _HEX64 = re.compile(r"^[0-9a-f]{64}$")
39
+ _DISTRIBUTION_NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
40
+ _TOOL_NAME = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
41
+ _RELEASE_MODES = {
42
+ "plan.llm_compiler.planner_component": "planner_component",
43
+ "plan.llm_compiler.full": "full",
44
+ }
45
+
46
+
47
+ class WorkerFailure(RuntimeError):
48
+ """One sanitized, typed failure returned over the JSONL boundary."""
49
+
50
+ def __init__(
51
+ self,
52
+ code: str,
53
+ message: str,
54
+ *,
55
+ consumed: bool = False,
56
+ usage: Mapping[str, int] | None = None,
57
+ ) -> None:
58
+ super().__init__(message)
59
+ self.code = code
60
+ self.public_message = message
61
+ self.consumed = consumed
62
+ self.usage = dict(usage) if usage is not None else None
63
+
64
+
65
+ class _DiscardTextIO(io.TextIOBase):
66
+ def write(self, value: str) -> int:
67
+ return len(value)
68
+
69
+ def flush(self) -> None:
70
+ return None
71
+
72
+
73
+ @dataclass(frozen=True, slots=True)
74
+ class Controls:
75
+ max_tasks: int
76
+ max_replans: int
77
+ concurrency: int
78
+ timeout_seconds: float
79
+ streaming: bool
80
+
81
+
82
+ @dataclass(frozen=True, slots=True)
83
+ class ValidatedRequest:
84
+ request_id: str
85
+ release_id: str
86
+ execution_mode: str
87
+ query: str
88
+ seed: int
89
+ prompt_hash: str
90
+ tool_specs: tuple[Mapping[str, Any], ...]
91
+ controls: Controls
92
+ backend_fingerprint: Mapping[str, Any]
93
+ tools_fingerprint: Mapping[str, Any]
94
+ tool_argument_order: Mapping[str, tuple[str, ...]] = field(default_factory=dict)
95
+
96
+
97
+ @dataclass(frozen=True, slots=True)
98
+ class LoadedUpstream:
99
+ planner: ModuleType
100
+ task_fetching: ModuleType
101
+ compiler: ModuleType
102
+ tools: ModuleType
103
+ logger: ModuleType
104
+
105
+
106
+ def _sha256_bytes(value: bytes) -> str:
107
+ return hashlib.sha256(value).hexdigest()
108
+
109
+
110
+ def _sha256_text(value: str) -> str:
111
+ return _sha256_bytes(value.encode("utf-8"))
112
+
113
+
114
+ def _canonical_json(value: Any) -> str:
115
+ return json.dumps(
116
+ value,
117
+ ensure_ascii=False,
118
+ sort_keys=True,
119
+ separators=(",", ":"),
120
+ allow_nan=False,
121
+ )
122
+
123
+
124
+ def _canonical_hash(value: Any) -> str:
125
+ return _sha256_text(_canonical_json(value))
126
+
127
+
128
+ def _read_bytes(path: Path, *, label: str, limit: int = _MAX_MANIFEST_BYTES) -> bytes:
129
+ if path.is_symlink() or not path.is_file():
130
+ raise WorkerFailure("MANIFEST_INVALID", f"{label} must be a regular non-symlink file")
131
+ try:
132
+ size = path.stat().st_size
133
+ except OSError as exc:
134
+ raise WorkerFailure("MANIFEST_INVALID", f"{label} is unreadable") from exc
135
+ if size > limit:
136
+ raise WorkerFailure("MANIFEST_INVALID", f"{label} exceeds the byte limit")
137
+ try:
138
+ return path.read_bytes()
139
+ except OSError as exc:
140
+ raise WorkerFailure("MANIFEST_INVALID", f"{label} is unreadable") from exc
141
+
142
+
143
+ def _load_json(path: Path, *, label: str) -> tuple[Mapping[str, Any], str]:
144
+ raw = _read_bytes(path, label=label)
145
+ try:
146
+ value = json.loads(raw)
147
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
148
+ raise WorkerFailure("MANIFEST_INVALID", f"{label} is not valid UTF-8 JSON") from exc
149
+ if not isinstance(value, Mapping):
150
+ raise WorkerFailure("MANIFEST_INVALID", f"{label} must contain a JSON object")
151
+ return value, _sha256_bytes(raw)
152
+
153
+
154
+ def _expect_keys(value: Mapping[str, Any], expected: set[str], *, label: str) -> None:
155
+ if set(value) != expected:
156
+ raise WorkerFailure("SCHEMA_INVALID", f"{label} contains unsupported or missing fields")
157
+
158
+
159
+ def _required_text(value: Mapping[str, Any], key: str, *, label: str) -> str:
160
+ result = value.get(key)
161
+ if not isinstance(result, str) or not result.strip():
162
+ raise WorkerFailure("SCHEMA_INVALID", f"{label}.{key} must be non-empty text")
163
+ return result
164
+
165
+
166
+ def _nonnegative_int(value: Any, *, label: str) -> int:
167
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
168
+ raise WorkerFailure("SCHEMA_INVALID", f"{label} must be a non-negative integer")
169
+ return value
170
+
171
+
172
+ def _positive_int(value: Any, *, label: str) -> int:
173
+ result = _nonnegative_int(value, label=label)
174
+ if result < 1:
175
+ raise WorkerFailure("SCHEMA_INVALID", f"{label} must be positive")
176
+ return result
177
+
178
+
179
+ def _verify_static_manifests() -> Mapping[str, Any]:
180
+ manifest, digest = _load_json(_MANIFEST_PATH, label="LLMCompiler compatibility manifest")
181
+ if digest != COMPATIBILITY_MANIFEST_SHA256:
182
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "compatibility manifest hash mismatch")
183
+ _expect_keys(
184
+ manifest,
185
+ {
186
+ "schema_version",
187
+ "upstream",
188
+ "source_files",
189
+ "legacy_dependencies",
190
+ "patch_manifest",
191
+ "execution_policy",
192
+ },
193
+ label="compatibility manifest",
194
+ )
195
+ if manifest["schema_version"] != "agentinputlab.llmcompiler.compatibility.v1":
196
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "compatibility schema mismatch")
197
+ upstream = manifest["upstream"]
198
+ if not isinstance(upstream, Mapping) or upstream.get("commit") != UPSTREAM_COMMIT:
199
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "pinned upstream identity mismatch")
200
+ patch = manifest["patch_manifest"]
201
+ if not isinstance(patch, Mapping) or set(patch) != {"path", "sha256"}:
202
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "patch manifest declaration is invalid")
203
+ if patch["path"] != "patch_manifest.json" or not _HEX64.fullmatch(str(patch["sha256"])):
204
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "patch manifest identity is invalid")
205
+ patch_path = _MANIFEST_PATH.parent / str(patch["path"])
206
+ if (
207
+ _sha256_bytes(_read_bytes(patch_path, label="LLMCompiler patch manifest"))
208
+ != patch["sha256"]
209
+ ):
210
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "patch manifest hash mismatch")
211
+ source_files = manifest["source_files"]
212
+ legacy = manifest["legacy_dependencies"]
213
+ if not isinstance(source_files, Mapping):
214
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "source hash manifest is invalid")
215
+ if not isinstance(legacy, Mapping) or set(legacy) != {
216
+ "requirements_sha256",
217
+ "required_versions",
218
+ }:
219
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "legacy dependency declaration is invalid")
220
+ requirements_sha = legacy["requirements_sha256"]
221
+ if (
222
+ not isinstance(requirements_sha, str)
223
+ or not _HEX64.fullmatch(requirements_sha)
224
+ or requirements_sha != source_files.get("requirements.txt")
225
+ ):
226
+ raise WorkerFailure(
227
+ "WORKER_INTEGRITY_FAILURE",
228
+ "legacy dependency requirements hash is invalid",
229
+ )
230
+ versions = legacy["required_versions"]
231
+ if not isinstance(versions, Mapping) or not versions:
232
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "required dependency versions are empty")
233
+ for distribution_name, version in versions.items():
234
+ if (
235
+ not isinstance(distribution_name, str)
236
+ or not _DISTRIBUTION_NAME.fullmatch(distribution_name)
237
+ or not isinstance(version, str)
238
+ or not version.strip()
239
+ ):
240
+ raise WorkerFailure(
241
+ "WORKER_INTEGRITY_FAILURE",
242
+ "required dependency version entry is invalid",
243
+ )
244
+ return manifest
245
+
246
+
247
+ def _verify_required_dependencies(manifest: Mapping[str, Any]) -> None:
248
+ """Verify every manifest-pinned distribution and its imported package origin."""
249
+
250
+ legacy = manifest["legacy_dependencies"]
251
+ assert isinstance(legacy, Mapping) # established by _verify_static_manifests
252
+ required_versions = legacy["required_versions"]
253
+ assert isinstance(required_versions, Mapping)
254
+ for distribution_name, expected_version in required_versions.items():
255
+ assert isinstance(distribution_name, str)
256
+ assert isinstance(expected_version, str)
257
+ import_name = distribution_name.replace("-", "_")
258
+ requirement = f"{distribution_name}=={expected_version}"
259
+ try:
260
+ distribution = importlib.metadata.distribution(distribution_name)
261
+ except importlib.metadata.PackageNotFoundError as exc:
262
+ raise WorkerFailure(
263
+ "LEGACY_DEPENDENCY_MISSING",
264
+ f"pinned LLMCompiler dependency is unavailable: {requirement}",
265
+ ) from exc
266
+ if distribution.version != expected_version:
267
+ raise WorkerFailure(
268
+ "LEGACY_DEPENDENCY_VERSION_MISMATCH",
269
+ f"pinned LLMCompiler requires {requirement}",
270
+ )
271
+ try:
272
+ module = importlib.import_module(import_name)
273
+ except ModuleNotFoundError as exc:
274
+ raise WorkerFailure(
275
+ "LEGACY_DEPENDENCY_MISSING",
276
+ f"pinned LLMCompiler dependency cannot be imported: {requirement}",
277
+ ) from exc
278
+ except Exception as exc:
279
+ raise WorkerFailure(
280
+ "LEGACY_DEPENDENCY_IMPORT_FAILURE",
281
+ f"pinned LLMCompiler dependency import failed: {requirement}",
282
+ ) from exc
283
+ module_file = getattr(module, "__file__", None)
284
+ try:
285
+ distribution_package = Path(distribution.locate_file(import_name)).resolve()
286
+ module_path = Path(module_file).resolve() if isinstance(module_file, str) else None
287
+ if module_path is None:
288
+ raise ValueError
289
+ module_path.relative_to(distribution_package)
290
+ except (OSError, TypeError, ValueError) as exc:
291
+ raise WorkerFailure(
292
+ "LEGACY_DEPENDENCY_ORIGIN_MISMATCH",
293
+ f"pinned LLMCompiler dependency escaped its distribution: {distribution_name}",
294
+ ) from exc
295
+
296
+
297
+ def _git(checkout: Path, *arguments: str) -> str:
298
+ environment = {
299
+ "PATH": os.environ.get("PATH", ""),
300
+ "LANG": "C",
301
+ "LC_ALL": "C",
302
+ "GIT_CONFIG_NOSYSTEM": "1",
303
+ "GIT_TERMINAL_PROMPT": "0",
304
+ }
305
+ try:
306
+ result = subprocess.run(
307
+ ("git", "-C", str(checkout), *arguments),
308
+ check=False,
309
+ capture_output=True,
310
+ text=True,
311
+ timeout=10,
312
+ env=environment,
313
+ )
314
+ except FileNotFoundError as exc:
315
+ raise WorkerFailure("GIT_UNAVAILABLE", "git is required to verify the checkout") from exc
316
+ except (OSError, subprocess.TimeoutExpired) as exc:
317
+ raise WorkerFailure("CHECKOUT_VERIFICATION_FAILED", "git verification failed") from exc
318
+ if result.returncode != 0:
319
+ raise WorkerFailure("CHECKOUT_VERIFICATION_FAILED", "git verification failed")
320
+ return result.stdout.strip()
321
+
322
+
323
+ def verify_checkout(checkout: Path) -> Mapping[str, Any]:
324
+ """Verify commit, cleanliness, license, source hashes, and patch attestation."""
325
+
326
+ manifest = _verify_static_manifests()
327
+ checkout = checkout.resolve()
328
+ if not checkout.is_dir():
329
+ raise WorkerFailure("CHECKOUT_MISSING", "the pinned LLMCompiler checkout is missing")
330
+ if _git(checkout, "rev-parse", "HEAD") != UPSTREAM_COMMIT:
331
+ raise WorkerFailure("CHECKOUT_REVISION_MISMATCH", "LLMCompiler checkout revision mismatch")
332
+ if _git(checkout, "status", "--porcelain=v1", "--untracked-files=all"):
333
+ raise WorkerFailure("CHECKOUT_DIRTY", "LLMCompiler checkout must be clean")
334
+ upstream = manifest["upstream"]
335
+ license_path = checkout / "LICENSE"
336
+ expected_license = str(upstream["license_sha256"])
337
+ if _sha256_bytes(_read_bytes(license_path, label="upstream LICENSE")) != expected_license:
338
+ raise WorkerFailure("LICENSE_MISMATCH", "LLMCompiler MIT license hash mismatch")
339
+ source_files = manifest["source_files"]
340
+ if not isinstance(source_files, Mapping) or not source_files:
341
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "source hash manifest is empty")
342
+ for relative, expected in source_files.items():
343
+ if not isinstance(relative, str) or not _HEX64.fullmatch(str(expected)):
344
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "source hash entry is invalid")
345
+ candidate = (checkout / relative).resolve()
346
+ try:
347
+ candidate.relative_to(checkout)
348
+ except ValueError as exc:
349
+ raise WorkerFailure("WORKER_INTEGRITY_FAILURE", "source path escapes checkout") from exc
350
+ actual = _sha256_bytes(_read_bytes(candidate, label=f"upstream source {relative}"))
351
+ if actual != expected:
352
+ raise WorkerFailure("SOURCE_MISMATCH", f"pinned upstream source mismatch: {relative}")
353
+ return manifest
354
+
355
+
356
+ def _validate_controls(value: Any) -> Controls:
357
+ if not isinstance(value, Mapping):
358
+ raise WorkerFailure("SCHEMA_INVALID", "payload.controls must be an object")
359
+ _expect_keys(
360
+ value,
361
+ {"max_tasks", "max_replans", "concurrency", "timeout_seconds", "streaming"},
362
+ label="payload.controls",
363
+ )
364
+ timeout = value["timeout_seconds"]
365
+ if (
366
+ isinstance(timeout, bool)
367
+ or not isinstance(timeout, (int, float))
368
+ or not math.isfinite(float(timeout))
369
+ or float(timeout) <= 0
370
+ ):
371
+ raise WorkerFailure("SCHEMA_INVALID", "controls.timeout_seconds must be positive")
372
+ streaming = value["streaming"]
373
+ if not isinstance(streaming, bool):
374
+ raise WorkerFailure("SCHEMA_INVALID", "controls.streaming must be boolean")
375
+ return Controls(
376
+ max_tasks=_positive_int(value["max_tasks"], label="controls.max_tasks"),
377
+ max_replans=_nonnegative_int(value["max_replans"], label="controls.max_replans"),
378
+ concurrency=_positive_int(value["concurrency"], label="controls.concurrency"),
379
+ timeout_seconds=float(timeout),
380
+ streaming=streaming,
381
+ )
382
+
383
+
384
+ def _validate_tool_specs(value: Any) -> tuple[Mapping[str, Any], ...]:
385
+ if isinstance(value, (str, bytes)) or not isinstance(value, Sequence) or not value:
386
+ raise WorkerFailure("SCHEMA_INVALID", "payload.tool_specs must be a non-empty list")
387
+ result: list[Mapping[str, Any]] = []
388
+ names: set[str] = set()
389
+ for index, item in enumerate(value):
390
+ if not isinstance(item, Mapping):
391
+ raise WorkerFailure("SCHEMA_INVALID", f"tool_specs[{index}] must be an object")
392
+ _expect_keys(
393
+ item,
394
+ {"name", "description", "input_schema", "read_only", "network"},
395
+ label=f"tool_specs[{index}]",
396
+ )
397
+ name = _required_text(item, "name", label=f"tool_specs[{index}]")
398
+ if not _TOOL_NAME.fullmatch(name) or name == "join" or name in names:
399
+ raise WorkerFailure("TOOL_POLICY_DENIED", "tool names must be unique safe identifiers")
400
+ if not isinstance(item["description"], str) or not item["description"].strip():
401
+ raise WorkerFailure("SCHEMA_INVALID", "tool description must be non-empty")
402
+ if not isinstance(item["input_schema"], Mapping):
403
+ raise WorkerFailure("SCHEMA_INVALID", "tool input_schema must be an object")
404
+ if item["read_only"] is not True or item["network"] is not False:
405
+ raise WorkerFailure(
406
+ "TOOL_POLICY_DENIED",
407
+ "LLMCompiler worker accepts only read-only, no-network tools",
408
+ )
409
+ try:
410
+ _canonical_json(item["input_schema"])
411
+ except (TypeError, ValueError) as exc:
412
+ raise WorkerFailure("SCHEMA_INVALID", "tool schema is not canonical JSON") from exc
413
+ names.add(name)
414
+ result.append(dict(item))
415
+ return tuple(result)
416
+
417
+
418
+ def validate_request(value: Any) -> ValidatedRequest:
419
+ if not isinstance(value, Mapping):
420
+ raise WorkerFailure("SCHEMA_INVALID", "worker request must be a JSON object")
421
+ _expect_keys(
422
+ value,
423
+ {
424
+ "method_id",
425
+ "release_id",
426
+ "upstream_commit",
427
+ "state_hash",
428
+ "payload",
429
+ "dependency_fingerprints",
430
+ "minimum_reservation",
431
+ "protocol_version",
432
+ "request_id",
433
+ },
434
+ label="request",
435
+ )
436
+ request_id = _required_text(value, "request_id", label="request")
437
+ if not _HEX64.fullmatch(request_id):
438
+ raise WorkerFailure("REQUEST_ID_MISMATCH", "request_id must be a SHA-256 digest")
439
+ identity = {key: value[key] for key in value if key != "request_id"}
440
+ try:
441
+ expected_request_id = _canonical_hash(identity)
442
+ except (TypeError, ValueError) as exc:
443
+ raise WorkerFailure("SCHEMA_INVALID", "request is not canonical JSON") from exc
444
+ if request_id != expected_request_id:
445
+ raise WorkerFailure("REQUEST_ID_MISMATCH", "request_id content hash mismatch")
446
+ if value["method_id"] != "llm_compiler":
447
+ raise WorkerFailure("IDENTITY_MISMATCH", "worker accepts only llm_compiler")
448
+ release_id = _required_text(value, "release_id", label="request")
449
+ if release_id not in _RELEASE_MODES:
450
+ raise WorkerFailure("IDENTITY_MISMATCH", "unsupported LLMCompiler release")
451
+ if value["upstream_commit"] != UPSTREAM_COMMIT:
452
+ raise WorkerFailure("IDENTITY_MISMATCH", "upstream commit mismatch")
453
+ if value["protocol_version"] != PROTOCOL_VERSION:
454
+ raise WorkerFailure("IDENTITY_MISMATCH", "protocol version mismatch")
455
+ _required_text(value, "state_hash", label="request")
456
+ payload = value["payload"]
457
+ if not isinstance(payload, Mapping):
458
+ raise WorkerFailure("SCHEMA_INVALID", "request.payload must be an object")
459
+ expected_payload = {
460
+ "task_id",
461
+ "query",
462
+ "instruction",
463
+ "messages",
464
+ "tool_specs",
465
+ "controls",
466
+ "prompt_hash",
467
+ "execution_mode",
468
+ "seed",
469
+ }
470
+ if (not expected_payload <= set(payload)
471
+ or set(payload) - expected_payload - {"execution_budget", "tool_argument_order"}):
472
+ raise WorkerFailure("SCHEMA_INVALID", "payload contains unsupported or missing fields")
473
+ query = _required_text(payload, "query", label="payload")
474
+ if payload["instruction"] != "" or payload["messages"] != []:
475
+ raise WorkerFailure(
476
+ "UNSUPPORTED_CONVERSATION_STATE",
477
+ "official LLMCompiler accepts a single query without instruction/history",
478
+ )
479
+ mode = _required_text(payload, "execution_mode", label="payload")
480
+ if mode != _RELEASE_MODES[release_id]:
481
+ raise WorkerFailure("IDENTITY_MISMATCH", "release and execution_mode disagree")
482
+ seed = _nonnegative_int(payload["seed"], label="payload.seed")
483
+ dependencies = value["dependency_fingerprints"]
484
+ if not isinstance(dependencies, Mapping) or set(dependencies) != {"backend", "tools"}:
485
+ raise WorkerFailure("SCHEMA_INVALID", "dependency fingerprints must bind backend/tools")
486
+ backend = dependencies["backend"]
487
+ tools = dependencies["tools"]
488
+ if not isinstance(backend, Mapping) or not isinstance(tools, Mapping):
489
+ raise WorkerFailure("SCHEMA_INVALID", "dependency fingerprints must be objects")
490
+ reservation = value["minimum_reservation"]
491
+ if (
492
+ not isinstance(reservation, Mapping)
493
+ or _nonnegative_int(reservation.get("model_calls"), label="minimum_reservation.model_calls")
494
+ < 1
495
+ ):
496
+ raise WorkerFailure("SCHEMA_INVALID", "worker requires a model-call reservation")
497
+ specs = _validate_tool_specs(payload["tool_specs"])
498
+ order = payload.get("tool_argument_order", {})
499
+ by_name = {spec["name"]: spec for spec in specs}
500
+ if (not isinstance(order, Mapping) or not set(order) <= set(by_name)
501
+ or any(not isinstance(keys, (list, tuple)) or not all(isinstance(k, str) for k in keys)
502
+ or len(set(keys)) != len(keys)
503
+ or set(keys) != set(by_name[name]["input_schema"].get("properties", {}))
504
+ for name, keys in order.items())):
505
+ raise WorkerFailure("SCHEMA_INVALID", "tool_argument_order must bind exact tool parameter names")
506
+ return ValidatedRequest(
507
+ request_id=request_id,
508
+ release_id=release_id,
509
+ execution_mode=mode,
510
+ query=query,
511
+ seed=seed,
512
+ prompt_hash=_required_text(payload, "prompt_hash", label="payload"),
513
+ tool_specs=specs,
514
+ controls=_validate_controls(payload["controls"]),
515
+ backend_fingerprint=dict(backend),
516
+ tools_fingerprint=dict(tools),
517
+ tool_argument_order={name: tuple(keys) for name, keys in order.items()},
518
+ )
519
+
520
+
521
+ def _fingerprint_sha(value: Mapping[str, Any], key: str, *, label: str) -> str:
522
+ result = value.get(key)
523
+ if not isinstance(result, str) or not _HEX64.fullmatch(result):
524
+ raise WorkerFailure("DEPENDENCY_MISMATCH", f"{label} must bind {key}")
525
+ return result
526
+
527
+
528
+ @dataclass(frozen=True, slots=True)
529
+ class ReplayEntry:
530
+ channel: str
531
+ prompt_sha256: str
532
+ response: str
533
+ input_tokens: int
534
+ output_tokens: int
535
+
536
+
537
+ @dataclass(frozen=True, slots=True)
538
+ class ReplayBundle:
539
+ prompts: Mapping[str, Any]
540
+ entries: tuple[ReplayEntry, ...]
541
+
542
+
543
+ def load_model_replay(path: Path, request: ValidatedRequest) -> ReplayBundle:
544
+ value, digest = _load_json(path, label="LLMCompiler model replay")
545
+ if digest != _fingerprint_sha(
546
+ request.backend_fingerprint,
547
+ "replay_manifest_sha256",
548
+ label="backend fingerprint",
549
+ ):
550
+ raise WorkerFailure("DEPENDENCY_MISMATCH", "model replay manifest hash mismatch")
551
+ _expect_keys(
552
+ value,
553
+ {"schema_version", "upstream_commit", "seed", "prompts", "prompt_hash", "responses"},
554
+ label="model replay",
555
+ )
556
+ if (
557
+ value["schema_version"] != "agentinputlab.llmcompiler.model-replay.v1"
558
+ or value["upstream_commit"] != UPSTREAM_COMMIT
559
+ or value["seed"] != request.seed
560
+ ):
561
+ raise WorkerFailure("DEPENDENCY_MISMATCH", "model replay identity mismatch")
562
+ prompts = value["prompts"]
563
+ if not isinstance(prompts, Mapping):
564
+ raise WorkerFailure("MANIFEST_INVALID", "model replay prompts must be an object")
565
+ _expect_keys(
566
+ prompts,
567
+ {
568
+ "planner_example_prompt",
569
+ "planner_example_prompt_replan",
570
+ "planner_stop",
571
+ "joinner_prompt",
572
+ "joinner_prompt_final",
573
+ },
574
+ label="model replay prompts",
575
+ )
576
+ if not all(
577
+ isinstance(prompts[key], str)
578
+ for key in (
579
+ "planner_example_prompt",
580
+ "planner_example_prompt_replan",
581
+ "joinner_prompt",
582
+ "joinner_prompt_final",
583
+ )
584
+ ):
585
+ raise WorkerFailure("MANIFEST_INVALID", "model replay prompt fields must be text")
586
+ stop = prompts["planner_stop"]
587
+ if (
588
+ isinstance(stop, (str, bytes))
589
+ or not isinstance(stop, Sequence)
590
+ or not all(isinstance(item, str) and item for item in stop)
591
+ ):
592
+ raise WorkerFailure("MANIFEST_INVALID", "planner_stop must be a list of strings")
593
+ computed_prompt_hash = _canonical_hash(prompts)
594
+ if value["prompt_hash"] != computed_prompt_hash or request.prompt_hash != computed_prompt_hash:
595
+ raise WorkerFailure("DEPENDENCY_MISMATCH", "prompt bundle hash mismatch")
596
+ responses = value["responses"]
597
+ if isinstance(responses, (str, bytes)) or not isinstance(responses, Sequence) or not responses:
598
+ raise WorkerFailure("MANIFEST_INVALID", "model replay responses must be non-empty")
599
+ entries: list[ReplayEntry] = []
600
+ for index, item in enumerate(responses):
601
+ if not isinstance(item, Mapping):
602
+ raise WorkerFailure("MANIFEST_INVALID", f"model replay response {index} is invalid")
603
+ _expect_keys(
604
+ item,
605
+ {
606
+ "channel",
607
+ "prompt_sha256",
608
+ "response",
609
+ "response_sha256",
610
+ "input_tokens",
611
+ "output_tokens",
612
+ },
613
+ label=f"model replay response {index}",
614
+ )
615
+ channel = item["channel"]
616
+ response = item["response"]
617
+ if channel not in {"planner", "join"} or not isinstance(response, str) or not response:
618
+ raise WorkerFailure("MANIFEST_INVALID", "model replay channel/response is invalid")
619
+ if not _HEX64.fullmatch(str(item["prompt_sha256"])):
620
+ raise WorkerFailure("MANIFEST_INVALID", "model replay prompt hash is invalid")
621
+ if item["response_sha256"] != _sha256_text(response):
622
+ raise WorkerFailure("MANIFEST_INVALID", "model replay response hash mismatch")
623
+ entries.append(
624
+ ReplayEntry(
625
+ channel=channel,
626
+ prompt_sha256=str(item["prompt_sha256"]),
627
+ response=response,
628
+ input_tokens=_nonnegative_int(
629
+ item["input_tokens"], label="model replay input_tokens"
630
+ ),
631
+ output_tokens=_nonnegative_int(
632
+ item["output_tokens"], label="model replay output_tokens"
633
+ ),
634
+ )
635
+ )
636
+ return ReplayBundle(prompts=dict(prompts), entries=tuple(entries))
637
+
638
+
639
+ @dataclass(slots=True)
640
+ class ToolReplay:
641
+ cases: Mapping[str, Any]
642
+ calls: int = 0
643
+
644
+ async def invoke(self, name: str, arguments: Sequence[Any]) -> Any:
645
+ self.calls += 1
646
+ try:
647
+ key = _canonical_hash(list(arguments))
648
+ except (TypeError, ValueError) as exc:
649
+ raise WorkerFailure(
650
+ "TOOL_ARGUMENT_DENIED",
651
+ "tool arguments are not canonical JSON",
652
+ consumed=True,
653
+ ) from exc
654
+ compound = f"{name}:{key}"
655
+ if compound not in self.cases:
656
+ raise WorkerFailure(
657
+ "TOOL_REPLAY_MISS",
658
+ "tool arguments are absent from the frozen allowlist",
659
+ consumed=True,
660
+ )
661
+ return self.cases[compound]
662
+
663
+
664
+ def load_tool_replay(path: Path, request: ValidatedRequest) -> ToolReplay:
665
+ value, digest = _load_json(path, label="LLMCompiler tool replay")
666
+ if digest != _fingerprint_sha(
667
+ request.tools_fingerprint,
668
+ "tool_manifest_sha256",
669
+ label="tools fingerprint",
670
+ ):
671
+ raise WorkerFailure("DEPENDENCY_MISMATCH", "tool replay manifest hash mismatch")
672
+ _expect_keys(value, {"schema_version", "upstream_commit", "tools"}, label="tool replay")
673
+ if (
674
+ value["schema_version"] != "agentinputlab.llmcompiler.tool-replay.v1"
675
+ or value["upstream_commit"] != UPSTREAM_COMMIT
676
+ ):
677
+ raise WorkerFailure("DEPENDENCY_MISMATCH", "tool replay identity mismatch")
678
+ tools = value["tools"]
679
+ if isinstance(tools, (str, bytes)) or not isinstance(tools, Sequence):
680
+ raise WorkerFailure("MANIFEST_INVALID", "tool replay tools must be a list")
681
+ by_name: dict[str, Mapping[str, Any]] = {}
682
+ cases: dict[str, Any] = {}
683
+ for index, item in enumerate(tools):
684
+ if not isinstance(item, Mapping):
685
+ raise WorkerFailure("MANIFEST_INVALID", f"tool replay item {index} is invalid")
686
+ _expect_keys(
687
+ item,
688
+ {
689
+ "name",
690
+ "description",
691
+ "input_schema",
692
+ "read_only",
693
+ "network",
694
+ "implementation",
695
+ "cases",
696
+ },
697
+ label=f"tool replay item {index}",
698
+ )
699
+ name = _required_text(item, "name", label=f"tool replay item {index}")
700
+ if name in by_name or not _TOOL_NAME.fullmatch(name) or name == "join":
701
+ raise WorkerFailure("MANIFEST_INVALID", "tool replay names are invalid or duplicated")
702
+ if (
703
+ item["read_only"] is not True
704
+ or item["network"] is not False
705
+ or item["implementation"] != "replay_table"
706
+ or not isinstance(item["input_schema"], Mapping)
707
+ ):
708
+ raise WorkerFailure("TOOL_POLICY_DENIED", "tool replay violates no-side-effect policy")
709
+ rows = item["cases"]
710
+ if isinstance(rows, (str, bytes)) or not isinstance(rows, Sequence):
711
+ raise WorkerFailure("MANIFEST_INVALID", "tool replay cases must be a list")
712
+ for row in rows:
713
+ if not isinstance(row, Mapping) or set(row) != {"arguments", "output"}:
714
+ raise WorkerFailure("MANIFEST_INVALID", "tool replay case is invalid")
715
+ arguments = row["arguments"]
716
+ if isinstance(arguments, (str, bytes)) or not isinstance(arguments, Sequence):
717
+ raise WorkerFailure("MANIFEST_INVALID", "tool replay arguments must be a list")
718
+ try:
719
+ key = f"{name}:{_canonical_hash(list(arguments))}"
720
+ _canonical_json(row["output"])
721
+ except (TypeError, ValueError) as exc:
722
+ raise WorkerFailure(
723
+ "MANIFEST_INVALID", "tool replay case is not canonical"
724
+ ) from exc
725
+ if key in cases:
726
+ raise WorkerFailure("MANIFEST_INVALID", "tool replay contains duplicate cases")
727
+ cases[key] = row["output"]
728
+ by_name[name] = item
729
+ requested_names = {str(item["name"]) for item in request.tool_specs}
730
+ if not requested_names <= set(by_name):
731
+ raise WorkerFailure(
732
+ "TOOL_POLICY_DENIED", "request contains a tool absent from the manifest"
733
+ )
734
+ for spec in request.tool_specs:
735
+ manifest_spec = by_name[str(spec["name"])]
736
+ comparable = {
737
+ key: manifest_spec[key]
738
+ for key in ("name", "description", "input_schema", "read_only", "network")
739
+ }
740
+ if _canonical_json(comparable) != _canonical_json(spec):
741
+ raise WorkerFailure("DEPENDENCY_MISMATCH", "tool schema differs from frozen manifest")
742
+ return ToolReplay(cases=cases)
743
+
744
+
745
+ class _ReplayModel:
746
+ def __init__(self, entries: Sequence[ReplayEntry]) -> None:
747
+ self._entries = list(entries)
748
+ self._cursor = 0
749
+ self.consumed: list[ReplayEntry] = []
750
+ self.planner_responses: list[str] = []
751
+
752
+ def usage(self, *, tool_calls: int = 0) -> dict[str, int]:
753
+ return {
754
+ "model_calls": len(self.consumed),
755
+ "retrieval_calls": 0,
756
+ "tool_calls": tool_calls,
757
+ "input_tokens": sum(item.input_tokens for item in self.consumed),
758
+ "output_tokens": sum(item.output_tokens for item in self.consumed),
759
+ }
760
+
761
+ def _consume(self, channel: str, prompt: str) -> ReplayEntry:
762
+ if self._cursor >= len(self._entries):
763
+ raise WorkerFailure(
764
+ "MODEL_REPLAY_EXHAUSTED",
765
+ "model replay has no response for this call",
766
+ consumed=bool(self.consumed),
767
+ usage=self.usage(),
768
+ )
769
+ entry = self._entries[self._cursor]
770
+ if entry.channel != channel or entry.prompt_sha256 != _sha256_text(prompt):
771
+ raise WorkerFailure(
772
+ "MODEL_PROMPT_MISMATCH",
773
+ "model replay prompt/channel mismatch",
774
+ consumed=bool(self.consumed),
775
+ usage=self.usage(),
776
+ )
777
+ self._cursor += 1
778
+ self.consumed.append(entry)
779
+ if channel == "planner":
780
+ self.planner_responses.append(entry.response)
781
+ return entry
782
+
783
+ @staticmethod
784
+ async def _notify(callbacks: Any, response: str) -> None:
785
+ if callbacks is None:
786
+ return
787
+ items = callbacks if isinstance(callbacks, Sequence) else (callbacks,)
788
+ run_id = uuid4()
789
+ for token in response:
790
+ for callback in items:
791
+ method = getattr(callback, "on_llm_new_token", None)
792
+ if callable(method):
793
+ pending = method(token, run_id=run_id)
794
+ if inspect.isawaitable(pending):
795
+ await pending
796
+ for callback in items:
797
+ method = getattr(callback, "on_llm_end", None)
798
+ if callable(method):
799
+ pending = method(None, run_id=run_id)
800
+ if inspect.isawaitable(pending):
801
+ await pending
802
+
803
+ async def apredict(
804
+ self,
805
+ prompt: str,
806
+ *,
807
+ callbacks: Any = None,
808
+ stop: Any = None,
809
+ **_: Any,
810
+ ) -> str:
811
+ del stop
812
+ entry = self._consume("planner", prompt)
813
+ await self._notify(callbacks, entry.response)
814
+ return entry.response
815
+
816
+ async def agenerate_prompt(
817
+ self,
818
+ prompts: Sequence[Any],
819
+ stop: Any = None,
820
+ callbacks: Any = None,
821
+ **_: Any,
822
+ ) -> Any:
823
+ del stop, callbacks
824
+ if len(prompts) != 1:
825
+ raise WorkerFailure("MODEL_REPLAY_INVALID_CALL", "join requires exactly one prompt")
826
+ value = prompts[0]
827
+ if hasattr(value, "text"):
828
+ prompt = value.text
829
+ elif callable(getattr(value, "to_string", None)):
830
+ prompt = value.to_string()
831
+ else:
832
+ prompt = str(value)
833
+ entry = self._consume("join", prompt)
834
+ generation = SimpleNamespace(text=entry.response)
835
+ return SimpleNamespace(generations=[[generation]])
836
+
837
+ def assert_exhausted(self) -> None:
838
+ if self._cursor != len(self._entries):
839
+ raise WorkerFailure(
840
+ "MODEL_REPLAY_UNUSED",
841
+ "model replay contains unused responses",
842
+ consumed=bool(self.consumed),
843
+ usage=self.usage(),
844
+ )
845
+
846
+
847
+ def _module_in_checkout(module: ModuleType, checkout: Path) -> bool:
848
+ location = getattr(module, "__file__", None)
849
+ if not isinstance(location, str):
850
+ return False
851
+ try:
852
+ Path(location).resolve().relative_to(checkout.resolve())
853
+ except ValueError:
854
+ return False
855
+ return True
856
+
857
+
858
+ def import_upstream(checkout: Path) -> LoadedUpstream:
859
+ """Import only fixed module names after checkout verification."""
860
+
861
+ checkout = checkout.resolve()
862
+ manifest = _verify_static_manifests()
863
+ _verify_required_dependencies(manifest)
864
+ sys.path.insert(0, str(checkout))
865
+ discarded = _DiscardTextIO()
866
+ try:
867
+ with contextlib.redirect_stdout(discarded):
868
+ task_module = importlib.import_module("src.llm_compiler.task_fetching_unit")
869
+ tools_module = importlib.import_module("src.tools.base")
870
+ planner_module = importlib.import_module("src.llm_compiler.planner")
871
+ compiler_module = importlib.import_module("src.llm_compiler.llm_compiler")
872
+ logger_module = importlib.import_module("src.utils.logger_utils")
873
+ except ModuleNotFoundError as exc:
874
+ missing = str(exc.name or "legacy dependency").split(".")[0]
875
+ raise WorkerFailure(
876
+ "LEGACY_DEPENDENCY_MISSING",
877
+ f"pinned LLMCompiler dependency is unavailable: {missing}",
878
+ ) from exc
879
+ except Exception as exc:
880
+ raise WorkerFailure(
881
+ "UPSTREAM_IMPORT_FAILURE",
882
+ f"pinned LLMCompiler import failed: {type(exc).__name__}",
883
+ ) from exc
884
+ modules = (task_module, tools_module, planner_module, compiler_module, logger_module)
885
+ if not all(_module_in_checkout(module, checkout) for module in modules):
886
+ raise WorkerFailure("UPSTREAM_IMPORT_FAILURE", "upstream module escaped pinned checkout")
887
+ enable_logging = getattr(logger_module, "enable_logging", None)
888
+ if callable(enable_logging):
889
+ enable_logging(False)
890
+ return LoadedUpstream(
891
+ planner=planner_module,
892
+ task_fetching=task_module,
893
+ compiler=compiler_module,
894
+ tools=tools_module,
895
+ logger=logger_module,
896
+ )
897
+
898
+
899
+ def _patch_model_type_gates(upstream: LoadedUpstream) -> None:
900
+ class _NeverChatModel:
901
+ pass
902
+
903
+ upstream.planner.BaseChatModel = _NeverChatModel
904
+ upstream.planner.BaseLLM = _ReplayModel
905
+ upstream.compiler.BaseChatModel = _NeverChatModel
906
+ upstream.compiler.BaseLLM = _ReplayModel
907
+
908
+
909
+ def _build_tools(
910
+ upstream: LoadedUpstream,
911
+ specs: Sequence[Mapping[str, Any]],
912
+ replay: ToolReplay,
913
+ ) -> list[Any]:
914
+ tool_type = getattr(upstream.tools, "Tool", None)
915
+ if tool_type is None:
916
+ raise WorkerFailure("UPSTREAM_API_MISMATCH", "upstream Tool is unavailable")
917
+ result: list[Any] = []
918
+ for spec in specs:
919
+ name = str(spec["name"])
920
+
921
+ async def invoke(*arguments: Any, _name: str = name) -> Any:
922
+ return await replay.invoke(_name, arguments)
923
+
924
+ try:
925
+ result.append(tool_type(name=name, func=invoke, description=str(spec["description"])))
926
+ except Exception as exc:
927
+ raise WorkerFailure(
928
+ "UPSTREAM_API_MISMATCH",
929
+ f"upstream Tool construction failed: {type(exc).__name__}",
930
+ ) from exc
931
+ return result
932
+
933
+
934
+ @dataclass(slots=True)
935
+ class TraceRecorder:
936
+ allowed_tools: frozenset[str]
937
+ max_tasks: int
938
+ concurrency: int
939
+ argument_order: Mapping[str, tuple[str, ...]] = field(default_factory=dict)
940
+ tasks: dict[int, Any] = field(default_factory=dict)
941
+ scheduled_ids: set[int] = field(default_factory=set)
942
+ dependency_graph: dict[int, set[int]] = field(default_factory=dict)
943
+ events: list[dict[str, Any]] = field(default_factory=list)
944
+ failure: WorkerFailure | None = None
945
+ active_tasks: set[asyncio.Task[Any]] = field(default_factory=set)
946
+ planner_tasks: set[asyncio.Task[Any]] = field(default_factory=set)
947
+
948
+ def register_batch(self, tasks: Mapping[Any, Any]) -> None:
949
+ """Validate one planner batch atomically before the scheduler can see it."""
950
+
951
+ if not isinstance(tasks, Mapping):
952
+ raise WorkerFailure("UPSTREAM_PLAN_INVALID", "upstream task batch is malformed")
953
+ normalized: list[tuple[int, Any, bool, set[int]]] = []
954
+ batch_ids: set[int] = set()
955
+ executable_count = 0
956
+ for key, task in tasks.items():
957
+ task_id = getattr(task, "idx", None)
958
+ name = getattr(task, "name", None)
959
+ dependencies = getattr(task, "dependencies", None)
960
+ is_join = bool(getattr(task, "is_join", False))
961
+ if (
962
+ isinstance(task_id, bool)
963
+ or not isinstance(task_id, int)
964
+ or task_id < 1
965
+ or isinstance(key, bool)
966
+ or not isinstance(key, int)
967
+ or key != task_id
968
+ or not isinstance(name, str)
969
+ or (is_join and name != "join")
970
+ or (not is_join and name not in self.allowed_tools)
971
+ or isinstance(dependencies, (str, bytes, Mapping))
972
+ or not isinstance(dependencies, (Sequence, set, frozenset))
973
+ ):
974
+ raise WorkerFailure("UPSTREAM_PLAN_INVALID", "upstream task is malformed")
975
+ dependency_tuple = tuple(dependencies)
976
+ if any(
977
+ isinstance(dependency, bool) or not isinstance(dependency, int) or dependency < 1
978
+ for dependency in dependency_tuple
979
+ ):
980
+ raise WorkerFailure("UPSTREAM_PLAN_INVALID", "upstream dependency is malformed")
981
+ dependency_ids = set(dependency_tuple)
982
+ if len(dependency_ids) != len(dependency_tuple) or task_id in dependency_ids:
983
+ raise WorkerFailure(
984
+ "UPSTREAM_PLAN_INVALID",
985
+ "upstream task has duplicate or self dependencies",
986
+ )
987
+ if task_id in self.scheduled_ids or task_id in batch_ids:
988
+ raise WorkerFailure("UPSTREAM_PLAN_INVALID", "upstream task IDs are duplicated")
989
+ batch_ids.add(task_id)
990
+ executable_count += int(not is_join)
991
+ normalized.append((task_id, task, is_join, dependency_ids))
992
+
993
+ if len(self.tasks) + executable_count > self.max_tasks:
994
+ raise WorkerFailure("TASK_LIMIT_EXCEEDED", "upstream plan exceeded max_tasks")
995
+ known_ids = self.scheduled_ids | batch_ids
996
+ if any(not dependencies <= known_ids for _, _, _, dependencies in normalized):
997
+ raise WorkerFailure(
998
+ "UPSTREAM_PLAN_INVALID",
999
+ "upstream task references an unknown dependency",
1000
+ )
1001
+ candidate_graph = {
1002
+ node: set(dependencies) for node, dependencies in self.dependency_graph.items()
1003
+ }
1004
+ candidate_graph.update(
1005
+ {task_id: set(dependencies) for task_id, _, _, dependencies in normalized}
1006
+ )
1007
+ visiting: set[int] = set()
1008
+ visited: set[int] = set()
1009
+
1010
+ def visit(node: int) -> None:
1011
+ if node in visiting:
1012
+ raise WorkerFailure("UPSTREAM_PLAN_INVALID", "upstream task graph has a cycle")
1013
+ if node in visited:
1014
+ return
1015
+ visiting.add(node)
1016
+ for dependency in candidate_graph[node]:
1017
+ visit(dependency)
1018
+ visiting.remove(node)
1019
+ visited.add(node)
1020
+
1021
+ for task_id in batch_ids:
1022
+ visit(task_id)
1023
+
1024
+ self.scheduled_ids.update(batch_ids)
1025
+ self.dependency_graph = candidate_graph
1026
+ for task_id, task, is_join, _ in normalized:
1027
+ if not is_join:
1028
+ self.tasks[task_id] = task
1029
+ self.emit(task_id, "planned")
1030
+
1031
+ def emit(self, task_id: int, state: str) -> None:
1032
+ self.events.append({"index": len(self.events), "task_id": task_id, "state": state})
1033
+
1034
+ def fail(self, task_id: int, exc: BaseException) -> None:
1035
+ if self.failure is None:
1036
+ self.failure = (
1037
+ exc
1038
+ if isinstance(exc, WorkerFailure)
1039
+ else WorkerFailure(
1040
+ "TOOL_EXECUTION_FAILURE",
1041
+ f"allowlisted tool failed: {type(exc).__name__}",
1042
+ consumed=True,
1043
+ )
1044
+ )
1045
+ self.emit(task_id, "failed")
1046
+
1047
+ def fail_planner(self, exc: BaseException) -> WorkerFailure:
1048
+ if self.failure is None:
1049
+ self.failure = (
1050
+ exc
1051
+ if isinstance(exc, WorkerFailure)
1052
+ else WorkerFailure(
1053
+ "PLANNER_STREAM_FAILURE",
1054
+ f"upstream streaming planner failed: {type(exc).__name__}",
1055
+ )
1056
+ )
1057
+ return self.failure
1058
+
1059
+ def task_rows(self, specs: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]:
1060
+ properties = {
1061
+ str(spec["name"]): self.argument_order.get(str(spec["name"]), tuple(spec["input_schema"].get("properties", {}).keys()))
1062
+ if isinstance(spec["input_schema"].get("properties", {}), Mapping)
1063
+ else ()
1064
+ for spec in specs
1065
+ }
1066
+ rows: list[dict[str, Any]] = []
1067
+ for task_id in sorted(self.tasks):
1068
+ task = self.tasks[task_id]
1069
+ arguments = list(getattr(task, "args", ()))
1070
+ names = properties.get(str(task.name), ())
1071
+ if len(arguments) == 1 and isinstance(arguments[0], Mapping):
1072
+ mapped_arguments = dict(arguments[0])
1073
+ elif len(names) == len(arguments) or (str(task.name) in self.argument_order and len(arguments) <= len(names)):
1074
+ mapped_arguments = dict(zip(names[:len(arguments)], arguments, strict=True))
1075
+ else:
1076
+ mapped_arguments = {"__args__": arguments}
1077
+ try:
1078
+ _canonical_json(mapped_arguments)
1079
+ except (TypeError, ValueError) as exc:
1080
+ raise WorkerFailure("UPSTREAM_PLAN_INVALID", "task arguments are not JSON") from exc
1081
+ rows.append(
1082
+ {
1083
+ "task_id": task_id,
1084
+ "tool_name": str(task.name),
1085
+ "arguments": mapped_arguments,
1086
+ "dependencies": sorted(getattr(task, "dependencies", ())),
1087
+ }
1088
+ )
1089
+ if not rows:
1090
+ raise WorkerFailure(
1091
+ "EMPTY_EXECUTABLE_PLAN", "plan contains only join/no executable task"
1092
+ )
1093
+ return rows
1094
+
1095
+
1096
+ def _traced_task_fetching_unit(base: type[Any], recorder: TraceRecorder) -> type[Any]:
1097
+ # Upstream creates a new TFU for each replan, but its parser uses global task
1098
+ # IDs (and join depends on every preceding ID). Make completed observations
1099
+ # available for dependency lookup without scheduling or iterating them again.
1100
+ # These maps belong to one _run_full invocation, never to another request.
1101
+ completed_tasks: dict[int, Any] = {}
1102
+ completed_events: dict[int, asyncio.Event] = {}
1103
+
1104
+ class RoundTasks(dict[int, Any]):
1105
+ def __missing__(self, task_id: int) -> Any:
1106
+ return completed_tasks[task_id]
1107
+
1108
+ class RoundEvents(dict[int, asyncio.Event]):
1109
+ def __missing__(self, task_id: int) -> asyncio.Event:
1110
+ return completed_events[task_id]
1111
+
1112
+ class TracedTaskFetchingUnit(base):
1113
+ def __init__(self) -> None:
1114
+ super().__init__()
1115
+ self.tasks = RoundTasks(self.tasks)
1116
+ self.tasks_done = RoundEvents(self.tasks_done)
1117
+ self._agentinputlab_semaphore = asyncio.Semaphore(recorder.concurrency)
1118
+
1119
+ def set_tasks(self, tasks: Mapping[Any, Any]) -> None:
1120
+ recorder.register_batch(tasks)
1121
+ super().set_tasks(tasks)
1122
+
1123
+ async def _run_task(self, task: Any) -> None:
1124
+ current = asyncio.current_task()
1125
+ if current is not None:
1126
+ recorder.active_tasks.add(current)
1127
+ if bool(getattr(task, "is_join", False)):
1128
+ await super()._run_task(task)
1129
+ completed_tasks[task.idx] = task
1130
+ completed_events[task.idx] = self.tasks_done[task.idx]
1131
+ return
1132
+ task_id = int(task.idx)
1133
+ recorder.emit(task_id, "ready")
1134
+ async with self._agentinputlab_semaphore:
1135
+ if recorder.failure is not None:
1136
+ return
1137
+ recorder.emit(task_id, "running")
1138
+ try:
1139
+ await super()._run_task(task)
1140
+ except asyncio.CancelledError:
1141
+ raise
1142
+ except BaseException as exc:
1143
+ recorder.fail(task_id, exc)
1144
+ for event in tuple(self.tasks_done.values()):
1145
+ event.set()
1146
+ self.remaining_tasks.clear()
1147
+ return
1148
+ recorder.emit(task_id, "completed")
1149
+ completed_tasks[task_id] = task
1150
+ completed_events[task_id] = self.tasks_done[task_id]
1151
+
1152
+ TracedTaskFetchingUnit.__name__ = "AgentInputLabTracedTaskFetchingUnit"
1153
+ return TracedTaskFetchingUnit
1154
+
1155
+
1156
+ async def _cancel_and_await(tasks: Sequence[asyncio.Task[Any]] | set[asyncio.Task[Any]]) -> None:
1157
+ current = asyncio.current_task()
1158
+ selected = tuple(dict.fromkeys(task for task in tasks if task is not current))
1159
+ for task in selected:
1160
+ if not task.done():
1161
+ task.cancel()
1162
+ if selected:
1163
+ await asyncio.gather(*selected, return_exceptions=True)
1164
+
1165
+
1166
+ async def _settle_planner_tasks(recorder: TraceRecorder) -> None:
1167
+ tasks = tuple(task for task in recorder.planner_tasks if task is not asyncio.current_task())
1168
+ if not tasks:
1169
+ return
1170
+ results = await asyncio.gather(*tasks, return_exceptions=True)
1171
+ for result in results:
1172
+ if isinstance(result, asyncio.CancelledError):
1173
+ recorder.fail_planner(
1174
+ WorkerFailure("PLANNER_STREAM_FAILURE", "upstream streaming planner was cancelled")
1175
+ )
1176
+ elif isinstance(result, BaseException):
1177
+ recorder.fail_planner(result)
1178
+
1179
+
1180
+ def _planner(
1181
+ upstream: LoadedUpstream,
1182
+ model: _ReplayModel,
1183
+ prompts: Mapping[str, Any],
1184
+ tools: Sequence[Any],
1185
+ ) -> Any:
1186
+ planner_type = getattr(upstream.planner, "Planner", None)
1187
+ if planner_type is None:
1188
+ raise WorkerFailure("UPSTREAM_API_MISMATCH", "upstream Planner is unavailable")
1189
+ return planner_type(
1190
+ llm=model,
1191
+ example_prompt=prompts["planner_example_prompt"],
1192
+ example_prompt_replan=prompts["planner_example_prompt_replan"],
1193
+ tools=tools,
1194
+ stop=list(prompts["planner_stop"]),
1195
+ )
1196
+
1197
+
1198
+ async def _run_planner_component(
1199
+ request: ValidatedRequest,
1200
+ upstream: LoadedUpstream,
1201
+ model: _ReplayModel,
1202
+ prompts: Mapping[str, Any],
1203
+ tools: Sequence[Any],
1204
+ ) -> tuple[list[Any], list[dict[str, Any]], int, str]:
1205
+ planner = _planner(upstream, model, prompts, tools)
1206
+ inputs = {"input": request.query}
1207
+ recorder = TraceRecorder(
1208
+ allowed_tools=frozenset(str(spec["name"]) for spec in request.tool_specs),
1209
+ max_tasks=request.controls.max_tasks,
1210
+ concurrency=request.controls.concurrency,
1211
+ argument_order=request.tool_argument_order,
1212
+ )
1213
+ if request.controls.streaming:
1214
+ queue: asyncio.Queue[Any] = asyncio.Queue()
1215
+
1216
+ async def produce() -> None:
1217
+ try:
1218
+ await planner.aplan(inputs=inputs, task_queue=queue, is_replan=False)
1219
+ except asyncio.CancelledError:
1220
+ raise
1221
+ except WorkerFailure:
1222
+ raise
1223
+ except Exception as exc:
1224
+ raise WorkerFailure(
1225
+ "PLANNER_STREAM_FAILURE",
1226
+ f"upstream streaming planner failed: {type(exc).__name__}",
1227
+ ) from exc
1228
+ finally:
1229
+ queue.put_nowait(None)
1230
+
1231
+ planner_task = asyncio.create_task(produce())
1232
+ recorder.planner_tasks.add(planner_task)
1233
+ try:
1234
+ while True:
1235
+ item = await queue.get()
1236
+ if item is None:
1237
+ break
1238
+ task_id = getattr(item, "idx", None)
1239
+ recorder.register_batch({task_id: item})
1240
+ await _settle_planner_tasks(recorder)
1241
+ if recorder.failure is not None:
1242
+ raise recorder.failure
1243
+ finally:
1244
+ await _cancel_and_await(recorder.planner_tasks)
1245
+ else:
1246
+ planned = await planner.plan(inputs=inputs, is_replan=False)
1247
+ if not isinstance(planned, Mapping):
1248
+ raise WorkerFailure("UPSTREAM_PLAN_INVALID", "Planner.plan did not return a task map")
1249
+ recorder.register_batch(planned)
1250
+ rows = recorder.task_rows(request.tool_specs)
1251
+ return rows, [], 0, ""
1252
+
1253
+
1254
+ async def _run_full(
1255
+ request: ValidatedRequest,
1256
+ upstream: LoadedUpstream,
1257
+ model: _ReplayModel,
1258
+ prompts: Mapping[str, Any],
1259
+ tools: Sequence[Any],
1260
+ ) -> tuple[list[Any], list[dict[str, Any]], int, str]:
1261
+ compiler_type = getattr(upstream.compiler, "LLMCompiler", None)
1262
+ base_tfu = getattr(upstream.task_fetching, "TaskFetchingUnit", None)
1263
+ if compiler_type is None or base_tfu is None:
1264
+ raise WorkerFailure("UPSTREAM_API_MISMATCH", "upstream full runtime is unavailable")
1265
+ recorder = TraceRecorder(
1266
+ allowed_tools=frozenset(str(spec["name"]) for spec in request.tool_specs),
1267
+ max_tasks=request.controls.max_tasks,
1268
+ concurrency=request.controls.concurrency,
1269
+ argument_order=request.tool_argument_order,
1270
+ )
1271
+ traced_tfu = _traced_task_fetching_unit(base_tfu, recorder)
1272
+ original_tfu = getattr(upstream.compiler, "TaskFetchingUnit", None)
1273
+ upstream.compiler.TaskFetchingUnit = traced_tfu
1274
+ try:
1275
+ compiler = compiler_type(
1276
+ tools=tools,
1277
+ planner_llm=model,
1278
+ planner_example_prompt=prompts["planner_example_prompt"],
1279
+ planner_example_prompt_replan=prompts["planner_example_prompt_replan"],
1280
+ planner_stop=list(prompts["planner_stop"]),
1281
+ planner_stream=request.controls.streaming,
1282
+ agent_llm=model,
1283
+ joinner_prompt=prompts["joinner_prompt"],
1284
+ joinner_prompt_final=prompts["joinner_prompt_final"],
1285
+ max_replans=request.controls.max_replans + 1,
1286
+ benchmark=False,
1287
+ )
1288
+ original_aplan = compiler.planner.aplan
1289
+
1290
+ async def guarded_aplan(*args: Any, **kwargs: Any) -> Any:
1291
+ current = asyncio.current_task()
1292
+ if current is not None:
1293
+ recorder.planner_tasks.add(current)
1294
+ task_queue = kwargs.get("task_queue")
1295
+ if task_queue is None and len(args) > 1:
1296
+ task_queue = args[1]
1297
+ try:
1298
+ return await original_aplan(*args, **kwargs)
1299
+ except asyncio.CancelledError:
1300
+ raise
1301
+ except BaseException as exc:
1302
+ raise recorder.fail_planner(exc) from exc
1303
+ finally:
1304
+ put_nowait = getattr(task_queue, "put_nowait", None)
1305
+ if callable(put_nowait):
1306
+ put_nowait(None)
1307
+
1308
+ compiler.planner.aplan = guarded_aplan
1309
+ original_join = compiler.join
1310
+
1311
+ async def guarded_join(*args: Any, **kwargs: Any) -> Any:
1312
+ await _settle_planner_tasks(recorder)
1313
+ if recorder.failure is not None:
1314
+ raise recorder.failure
1315
+ return await original_join(*args, **kwargs)
1316
+
1317
+ object.__setattr__(compiler, "join", guarded_join)
1318
+ result = await compiler._acall({"input": request.query})
1319
+ finally:
1320
+ upstream.compiler.TaskFetchingUnit = original_tfu
1321
+ await _cancel_and_await(recorder.planner_tasks | recorder.active_tasks)
1322
+ if recorder.failure is not None:
1323
+ raise recorder.failure
1324
+ if not isinstance(result, Mapping) or not isinstance(result.get("output"), str):
1325
+ raise WorkerFailure("UPSTREAM_OUTPUT_INVALID", "LLMCompiler returned an invalid output")
1326
+ answer = result["output"].strip()
1327
+ if not answer:
1328
+ raise WorkerFailure("UPSTREAM_OUTPUT_INVALID", "LLMCompiler returned an empty answer")
1329
+ replans = max(0, len(model.planner_responses) - 1)
1330
+ if replans > request.controls.max_replans:
1331
+ raise WorkerFailure("REPLAN_LIMIT_EXCEEDED", "LLMCompiler exceeded max_replans")
1332
+ return recorder.task_rows(request.tool_specs), recorder.events, replans, answer
1333
+
1334
+
1335
+ async def execute_request(
1336
+ request: ValidatedRequest,
1337
+ upstream: LoadedUpstream,
1338
+ model_bundle: ReplayBundle,
1339
+ tool_replay: ToolReplay,
1340
+ *,
1341
+ runtime_model: _ReplayModel | None = None,
1342
+ ) -> Mapping[str, Any]:
1343
+ runtime: dict[str, _ReplayModel] = {}
1344
+
1345
+ async def run() -> Mapping[str, Any]:
1346
+ _patch_model_type_gates(upstream)
1347
+ model = runtime_model if runtime_model is not None else _ReplayModel(model_bundle.entries)
1348
+ runtime["model"] = model
1349
+ tools = _build_tools(upstream, request.tool_specs, tool_replay)
1350
+ discarded = _DiscardTextIO()
1351
+ try:
1352
+ with contextlib.redirect_stdout(discarded):
1353
+ if request.execution_mode == "planner_component":
1354
+ tasks, events, replans, final_answer = await _run_planner_component(
1355
+ request, upstream, model, model_bundle.prompts, tools
1356
+ )
1357
+ termination = "planner_complete"
1358
+ else:
1359
+ tasks, events, replans, final_answer = await _run_full(
1360
+ request, upstream, model, model_bundle.prompts, tools
1361
+ )
1362
+ termination = "finish"
1363
+ model.assert_exhausted()
1364
+ except WorkerFailure as exc:
1365
+ if model.consumed or tool_replay.calls:
1366
+ exc.usage = model.usage(tool_calls=tool_replay.calls)
1367
+ exc.consumed = True
1368
+ raise
1369
+ except Exception as exc:
1370
+ raise WorkerFailure(
1371
+ "UPSTREAM_RUNTIME_FAILURE",
1372
+ f"pinned LLMCompiler runtime failed: {type(exc).__name__}",
1373
+ consumed=bool(model.consumed or tool_replay.calls),
1374
+ usage=model.usage(tool_calls=tool_replay.calls),
1375
+ ) from exc
1376
+ raw_plan_hash = _canonical_hash(model.planner_responses)
1377
+ return {
1378
+ "status": "ok",
1379
+ "request_id": request.request_id,
1380
+ "upstream_commit": UPSTREAM_COMMIT,
1381
+ "protocol_version": PROTOCOL_VERSION,
1382
+ "payload": {
1383
+ "tasks": tasks,
1384
+ "scheduler_events": events,
1385
+ "replans": replans,
1386
+ "streaming": request.controls.streaming,
1387
+ "raw_plan_hash": raw_plan_hash,
1388
+ "termination": termination,
1389
+ "final_answer": final_answer,
1390
+ },
1391
+ "usage": model.usage(tool_calls=tool_replay.calls),
1392
+ }
1393
+
1394
+ try:
1395
+ return await asyncio.wait_for(run(), timeout=request.controls.timeout_seconds)
1396
+ # Python 3.10 uses a distinct asyncio timeout exception; 3.11+ aliases it.
1397
+ except asyncio.TimeoutError as exc:
1398
+ model = runtime.get("model")
1399
+ usage = (
1400
+ model.usage(tool_calls=tool_replay.calls)
1401
+ if model is not None
1402
+ else {
1403
+ "model_calls": 0,
1404
+ "retrieval_calls": 0,
1405
+ "tool_calls": tool_replay.calls,
1406
+ "input_tokens": 0,
1407
+ "output_tokens": 0,
1408
+ }
1409
+ )
1410
+ raise WorkerFailure(
1411
+ "RUN_TIMEOUT",
1412
+ "LLMCompiler exceeded controls.timeout_seconds",
1413
+ consumed=bool(usage["model_calls"] or usage["tool_calls"]),
1414
+ usage=usage,
1415
+ ) from exc
1416
+
1417
+
1418
+ def _failure_response(exc: WorkerFailure, *, request_id: str = "") -> Mapping[str, Any]:
1419
+ response: dict[str, Any] = {
1420
+ "status": "error",
1421
+ "failure_code": exc.code,
1422
+ "message": exc.public_message,
1423
+ "consumed": exc.consumed,
1424
+ }
1425
+ if request_id:
1426
+ response["request_id"] = request_id
1427
+ if exc.usage is not None:
1428
+ response["usage"] = exc.usage
1429
+ return response
1430
+
1431
+
1432
+ def process_request(
1433
+ raw_request: Any,
1434
+ *,
1435
+ checkout: Path,
1436
+ model_replay_path: Path,
1437
+ tool_replay_path: Path,
1438
+ upstream: LoadedUpstream | None = None,
1439
+ ) -> Mapping[str, Any]:
1440
+ verify_checkout(checkout)
1441
+ request = validate_request(raw_request)
1442
+ model_bundle = load_model_replay(model_replay_path, request)
1443
+ tool_replay = load_tool_replay(tool_replay_path, request)
1444
+ loaded = upstream if upstream is not None else import_upstream(checkout)
1445
+ return asyncio.run(execute_request(request, loaded, model_bundle, tool_replay))
1446
+
1447
+
1448
+ def _read_request_line() -> Any:
1449
+ raw = sys.stdin.buffer.readline(_MAX_MANIFEST_BYTES + 1)
1450
+ if len(raw) > _MAX_MANIFEST_BYTES:
1451
+ raise WorkerFailure("REQUEST_TOO_LARGE", "worker request exceeds the byte limit")
1452
+ if not raw:
1453
+ raise WorkerFailure("SCHEMA_INVALID", "worker requires one JSON line")
1454
+ if sys.stdin.buffer.read(1):
1455
+ raise WorkerFailure("SCHEMA_INVALID", "worker accepts exactly one JSON line")
1456
+ try:
1457
+ return json.loads(raw)
1458
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
1459
+ raise WorkerFailure("SCHEMA_INVALID", "worker request is not valid UTF-8 JSON") from exc
1460
+
1461
+
1462
+ def _parser() -> argparse.ArgumentParser:
1463
+ parser = argparse.ArgumentParser(description=__doc__)
1464
+ parser.add_argument("--checkout", type=Path, required=True)
1465
+ parser.add_argument("--model-replay", type=Path)
1466
+ parser.add_argument("--tool-replay", type=Path)
1467
+ parser.add_argument("--smoke-import", action="store_true")
1468
+ return parser
1469
+
1470
+
1471
+ def main(argv: Sequence[str] | None = None) -> int:
1472
+ args = _parser().parse_args(argv)
1473
+ request_id = ""
1474
+ try:
1475
+ verify_checkout(args.checkout)
1476
+ if args.smoke_import:
1477
+ import_upstream(args.checkout)
1478
+ response: Mapping[str, Any] = {
1479
+ "status": "ok",
1480
+ "upstream_commit": UPSTREAM_COMMIT,
1481
+ "protocol_version": PROTOCOL_VERSION,
1482
+ "smoke_import": True,
1483
+ }
1484
+ else:
1485
+ if args.model_replay is None or args.tool_replay is None:
1486
+ raise WorkerFailure(
1487
+ "CONFIGURATION_MISSING",
1488
+ "normal execution requires --model-replay and --tool-replay",
1489
+ )
1490
+ raw_request = _read_request_line()
1491
+ if isinstance(raw_request, Mapping) and isinstance(raw_request.get("request_id"), str):
1492
+ request_id = str(raw_request["request_id"])
1493
+ request = validate_request(raw_request)
1494
+ model_bundle = load_model_replay(args.model_replay, request)
1495
+ tool_replay = load_tool_replay(args.tool_replay, request)
1496
+ upstream = import_upstream(args.checkout)
1497
+ response = asyncio.run(execute_request(request, upstream, model_bundle, tool_replay))
1498
+ except WorkerFailure as exc:
1499
+ response = _failure_response(exc, request_id=request_id)
1500
+ except Exception as exc: # pragma: no cover - last-resort JSONL containment
1501
+ response = _failure_response(
1502
+ WorkerFailure(
1503
+ "WORKER_INTERNAL_FAILURE",
1504
+ f"worker failed closed: {type(exc).__name__}",
1505
+ ),
1506
+ request_id=request_id,
1507
+ )
1508
+ sys.stdout.write(_canonical_json(response) + "\n")
1509
+ sys.stdout.flush()
1510
+ return 0 if response.get("status") == "ok" else 2
1511
+
1512
+
1513
+ if __name__ == "__main__":
1514
+ raise SystemExit(main())