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,1643 @@
1
+ #!/usr/bin/env python3
2
+ """Strict JSONL worker for pinned SmartSearch-3B frozen inference.
3
+
4
+ The worker executes the byte-verified upstream ``ReCall.run`` loop directly.
5
+ It replaces only two side-effect seams: the model POST is routed to a pinned
6
+ SGLang/OpenAI-completions deployment, and the upstream arbitrary-code executor
7
+ is replaced by a fixed Wikipedia-search call. The upstream sandbox is never
8
+ imported, started, or sent an ``env``/``call`` string.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import ast
14
+ import builtins
15
+ import hashlib
16
+ import json
17
+ import os
18
+ import re
19
+ import subprocess
20
+ import sys
21
+ import tempfile
22
+ import types
23
+ import urllib.error
24
+ import urllib.parse
25
+ import urllib.request
26
+ from collections.abc import Callable, Mapping, Sequence
27
+ from dataclasses import dataclass
28
+ from pathlib import Path
29
+ from typing import Any, TextIO
30
+
31
+ UPSTREAM_COMMIT = "5e9e7ec0d6af34f45fad145d4bff442529e7e6ff"
32
+ CHECKPOINT_REPOSITORY = "vvv111222/SmartSearch-3B"
33
+ CHECKPOINT_REVISION = "82bfe8a845e3e08f0d9d00d8e1e2cfb6d50b5da8"
34
+ PROTOCOL = "agentinputlab.smartsearch.frozen.v2"
35
+ RELEASE = "recipe.smartsearch.frozen_3b_inference"
36
+ PROFILE_ID = "smartsearch.controlled_frozen.v1"
37
+ MANIFEST_SCHEMA = "agentinputlab.smartsearch.compatibility.v1"
38
+ TOOL_SCHEMA_SHA256 = "a1349ee4630e42cff2a0631252bdf931d3621f2f1a5bb005430e1c0494508731"
39
+ LOOP_CONTRACT_SHA256 = "8c64cda35d8b08f31c7dd8fba2efbb4e7adf3d57dc3a59a8a8e1c41e64d5e5b2"
40
+ PARSER_CONTRACT_SHA256 = "b2104370b1b39a1a34b809c25fd369a5e97426a70bf55300de5f5dec8bf3af6a"
41
+ DEFAULT_MANIFEST = Path(__file__).with_name("compatibility_manifest.json")
42
+
43
+ CHECKOUT_ENV = "AGENTINPUTLAB_SMARTSEARCH_CHECKOUT"
44
+ RUNTIME_MANIFEST_ENV = "AGENTINPUTLAB_SMARTSEARCH_RUNTIME_MANIFEST"
45
+ SOURCE_ARCHIVE_ENV = "AGENTINPUTLAB_SMARTSEARCH_SOURCE_ARCHIVE"
46
+ SNAPSHOT_COMMIT_ENV = "AGENTINPUTLAB_SMARTSEARCH_SNAPSHOT_COMMIT"
47
+ MODEL_API_KEY_ENV = "AGENTINPUTLAB_SMARTSEARCH_MODEL_API_KEY"
48
+ RETRIEVER_API_KEY_ENV = "AGENTINPUTLAB_SMARTSEARCH_RETRIEVER_API_KEY"
49
+ SANDBOX_ENV = "AGENTINPUTLAB_SMARTSEARCH_SANDBOX_URL"
50
+ LOCAL_RETRIEVER_TRANSPORT = "agentinputlab_prepared_corpus_v1"
51
+ LOCAL_MODEL_TRANSPORT = "openai_completions_local_snapshot"
52
+
53
+ MAX_REQUEST_BYTES = 8 * 1024 * 1024
54
+ MAX_RESPONSE_BYTES = 32 * 1024 * 1024
55
+ MAX_TEXT_CHARS = 8 * 1024 * 1024
56
+ _HEX = frozenset("0123456789abcdef")
57
+ _PROTOCOL_TAG = re.compile(r"</?(?:search|result|answer|think)>", re.IGNORECASE)
58
+ _TOP_FIELDS = frozenset(
59
+ {
60
+ "method_id",
61
+ "release_id",
62
+ "upstream_commit",
63
+ "state_hash",
64
+ "payload",
65
+ "dependency_fingerprints",
66
+ "minimum_reservation",
67
+ "protocol_version",
68
+ "request_id",
69
+ }
70
+ )
71
+ _PAYLOAD_FIELDS = frozenset(
72
+ {
73
+ "profile_id",
74
+ "task_id",
75
+ "question",
76
+ "artifacts",
77
+ "controls",
78
+ "remaining_budget",
79
+ "seed",
80
+ "inference_contract",
81
+ }
82
+ )
83
+ _ARTIFACT_FIELDS = frozenset(
84
+ {
85
+ "model_snapshot_manifest_sha256",
86
+ "model_snapshot_tree_sha256",
87
+ "tokenizer_revision",
88
+ "served_model_name",
89
+ "model_deployment_fingerprint_sha256",
90
+ "context_admission_fingerprint_sha256",
91
+ "retriever_revision",
92
+ "retriever_deployment_fingerprint_sha256",
93
+ "corpus_sha256",
94
+ "index_sha256",
95
+ "retrieval_config_sha256",
96
+ "compatibility_manifest_sha256",
97
+ "model_transport",
98
+ "checkpoint_repository",
99
+ "checkpoint_revision",
100
+ "profile_id",
101
+ "tool_schema_sha256",
102
+ "loop_contract_sha256",
103
+ "parser_contract_sha256",
104
+ }
105
+ )
106
+ _CONTRACT_FIELDS = frozenset(
107
+ {
108
+ "profile_id",
109
+ "tool_schema_sha256",
110
+ "loop_contract_sha256",
111
+ "parser_contract_sha256",
112
+ "max_rounds",
113
+ "top_n",
114
+ "max_new_tokens",
115
+ "temperature",
116
+ "stop",
117
+ "outer_retry_attempts",
118
+ "search_tags_per_turn",
119
+ }
120
+ )
121
+ _FORBIDDEN_KEYS = frozenset(
122
+ {
123
+ "gold",
124
+ "gold_answer",
125
+ "ground_truth",
126
+ "label",
127
+ "labels",
128
+ "reward",
129
+ "rewards",
130
+ "process_reward",
131
+ "reference_answer",
132
+ "target_answer",
133
+ "verifier_score",
134
+ "endpoint",
135
+ "url",
136
+ "command",
137
+ "callable",
138
+ "import",
139
+ "api_key",
140
+ "credential",
141
+ "sandbox",
142
+ }
143
+ )
144
+
145
+
146
+ class DriverError(RuntimeError):
147
+ def __init__(self, code: str, message: str, *, consumed: bool = False) -> None:
148
+ super().__init__(message)
149
+ self.code = code
150
+ self.public_message = message
151
+ self.consumed = consumed
152
+
153
+
154
+ class _UnboundRequestsModule(types.ModuleType):
155
+ """Fail-closed stand-in for the upstream module's requests import."""
156
+
157
+ def __init__(self) -> None:
158
+ super().__init__("requests")
159
+
160
+ def post(self, *_args: Any, **_kwargs: Any) -> None:
161
+ raise DriverError(
162
+ "UPSTREAM_TRANSPORT_UNBOUND",
163
+ "the upstream requests seam is not bound to the controlled model transport",
164
+ )
165
+
166
+
167
+ @dataclass(slots=True)
168
+ class Usage:
169
+ model_calls: int = 0
170
+ retrieval_calls: int = 0
171
+ input_tokens: int = 0
172
+ output_tokens: int = 0
173
+
174
+ def as_mapping(self) -> Mapping[str, int]:
175
+ return {
176
+ "model_calls": self.model_calls,
177
+ "retrieval_calls": self.retrieval_calls,
178
+ "input_tokens": self.input_tokens,
179
+ "output_tokens": self.output_tokens,
180
+ }
181
+
182
+
183
+ class BudgetGate:
184
+ def __init__(self, caps: Mapping[str, Any]) -> None:
185
+ self.caps = caps
186
+ self.usage = Usage()
187
+
188
+ def before_model(self, admitted_input_tokens: int | None) -> None:
189
+ if self.usage.model_calls >= self.caps["model_calls"]:
190
+ raise DriverError("MODEL_BUDGET_EXHAUSTED", "model budget exhausted")
191
+ remaining_output = self.caps["output_tokens"] - self.usage.output_tokens
192
+ if remaining_output < 8192:
193
+ raise DriverError(
194
+ "OUTPUT_BUDGET_EXHAUSTED",
195
+ "8192 output tokens must remain before a SmartSearch model call",
196
+ )
197
+ context_cap = self.caps["context_tokens"]
198
+ if context_cap is not None:
199
+ if admitted_input_tokens is None:
200
+ raise DriverError(
201
+ "CONTEXT_ADMISSION_REQUIRED",
202
+ "an exact tokenizer admission result is required before model execution",
203
+ )
204
+ if self.usage.input_tokens + admitted_input_tokens > context_cap:
205
+ raise DriverError(
206
+ "CONTEXT_BUDGET_EXHAUSTED",
207
+ "prompt is rejected before model execution by the context budget",
208
+ )
209
+ self.usage.model_calls += 1
210
+
211
+ def after_model(
212
+ self,
213
+ input_tokens: int,
214
+ output_tokens: int,
215
+ *,
216
+ admitted_input_tokens: int | None,
217
+ ) -> None:
218
+ self.usage.input_tokens += input_tokens
219
+ self.usage.output_tokens += output_tokens
220
+ if admitted_input_tokens is not None and input_tokens != admitted_input_tokens:
221
+ raise DriverError(
222
+ "CONTEXT_ADMISSION_MISMATCH",
223
+ "provider usage disagrees with the preflight tokenizer admission",
224
+ consumed=True,
225
+ )
226
+ context_cap = self.caps["context_tokens"]
227
+ if context_cap is not None and self.usage.input_tokens > context_cap:
228
+ raise DriverError(
229
+ "CONTEXT_BUDGET_EXCEEDED",
230
+ "model-reported context token usage exceeded the supplied budget",
231
+ consumed=True,
232
+ )
233
+ if self.usage.output_tokens > self.caps["output_tokens"]:
234
+ raise DriverError(
235
+ "OUTPUT_BUDGET_EXCEEDED",
236
+ "model-reported output token usage exceeded the supplied budget",
237
+ consumed=True,
238
+ )
239
+
240
+ def before_retrieval(self) -> None:
241
+ if self.usage.retrieval_calls >= self.caps["retrieval_calls"]:
242
+ raise DriverError(
243
+ "RETRIEVAL_BUDGET_EXHAUSTED",
244
+ "retrieval budget exhausted",
245
+ consumed=bool(self.usage.model_calls),
246
+ )
247
+ self.usage.retrieval_calls += 1
248
+
249
+
250
+ @dataclass(frozen=True, slots=True)
251
+ class ModelTurn:
252
+ text: str
253
+ finish_reason: str
254
+ matched: str
255
+ input_tokens: int
256
+ output_tokens: int
257
+
258
+
259
+ class _NoRedirect(urllib.request.HTTPRedirectHandler):
260
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
261
+ del req, fp, code, msg, headers, newurl
262
+ raise DriverError("REDIRECT_REFUSED", "HTTP redirects are disabled", consumed=True)
263
+
264
+
265
+ class _HTTPJSON:
266
+ def __init__(self, *, api_key: str, timeout: float) -> None:
267
+ self.api_key = api_key
268
+ self.timeout = timeout
269
+ self.opener = urllib.request.build_opener(_NoRedirect())
270
+
271
+ def post(self, endpoint: str, body: Mapping[str, Any]) -> Any:
272
+ parsed = urllib.parse.urlsplit(endpoint)
273
+ if parsed.scheme not in {"http", "https"} or not parsed.netloc:
274
+ raise DriverError("ENDPOINT_INVALID", "endpoint must be an HTTP(S) URL")
275
+ if parsed.username or parsed.password or parsed.query or parsed.fragment:
276
+ raise DriverError("ENDPOINT_INVALID", "endpoint credentials/query are forbidden")
277
+ if self.api_key and parsed.scheme != "https":
278
+ raise DriverError("ENDPOINT_INVALID", "API keys may only be sent over HTTPS")
279
+ headers = {"Content-Type": "application/json", "Accept": "application/json"}
280
+ if self.api_key:
281
+ headers["Authorization"] = f"Bearer {self.api_key}"
282
+ request = urllib.request.Request(
283
+ endpoint,
284
+ data=_canonical_json(body).encode("utf-8"),
285
+ headers=headers,
286
+ method="POST",
287
+ )
288
+ try:
289
+ with self.opener.open(request, timeout=self.timeout) as response:
290
+ raw = response.read(MAX_RESPONSE_BYTES + 1)
291
+ except DriverError:
292
+ raise
293
+ except urllib.error.HTTPError as exc:
294
+ raise DriverError(
295
+ "HTTP_FAILED", f"endpoint returned HTTP {exc.code}", consumed=True
296
+ ) from exc
297
+ except Exception as exc:
298
+ raise DriverError("HTTP_FAILED", "endpoint request failed", consumed=True) from exc
299
+ if len(raw) > MAX_RESPONSE_BYTES:
300
+ raise DriverError(
301
+ "RESPONSE_TOO_LARGE", "endpoint response exceeds byte limit", consumed=True
302
+ )
303
+ try:
304
+ return _strict_json_loads(raw)
305
+ except (UnicodeDecodeError, json.JSONDecodeError, DriverError) as exc:
306
+ raise DriverError(
307
+ "RESPONSE_INVALID", "endpoint returned invalid JSON", consumed=True
308
+ ) from exc
309
+
310
+
311
+ class HTTPModelTransport:
312
+ def __init__(self, config: Mapping[str, Any], *, api_key: str = "") -> None:
313
+ self.mode = _text(config, "model_transport")
314
+ self.base_url = _http_base(_text(config, "model_base_url"))
315
+ self.tokenizer_base_url = _http_base(_text(config, "tokenizer_base_url"))
316
+ self.model = _text(config, "served_model_name")
317
+ self.identity = {
318
+ key: config[key]
319
+ for key in (
320
+ "checkpoint_repository",
321
+ "checkpoint_revision",
322
+ "model_snapshot_tree_sha256",
323
+ "tokenizer_revision",
324
+ "served_model_name",
325
+ "context_admission_fingerprint_sha256",
326
+ )
327
+ }
328
+ self.http = _HTTPJSON(
329
+ api_key=api_key, timeout=_positive_float(config, "model_timeout_seconds")
330
+ )
331
+
332
+ def fingerprint(self) -> Mapping[str, Any]:
333
+ return {
334
+ "transport": self.mode,
335
+ "base_url": self.base_url,
336
+ "tokenizer_base_url": self.tokenizer_base_url,
337
+ "identity": self.identity,
338
+ }
339
+
340
+ def count_prompt_tokens(self, prompt: str) -> int:
341
+ result = self.http.post(
342
+ self.tokenizer_base_url + "/tokenize",
343
+ {
344
+ "model": self.model,
345
+ "tokenizer_revision": self.identity["tokenizer_revision"],
346
+ "text": prompt,
347
+ },
348
+ )
349
+ if not isinstance(result, Mapping) or result.get("identity") != self.identity:
350
+ raise DriverError(
351
+ "CONTEXT_ADMISSION_IDENTITY_MISMATCH",
352
+ "tokenizer admission identity mismatch",
353
+ consumed=True,
354
+ )
355
+ return _nonnegative(result.get("input_tokens"), "admitted input_tokens")
356
+
357
+ def generate(self, prompt: str, sampling: Mapping[str, Any]) -> ModelTurn:
358
+ if self.mode == "sglang_generate":
359
+ result = self.http.post(
360
+ self.base_url + "/generate",
361
+ {"text": prompt, "sampling_params": dict(sampling)},
362
+ )
363
+ if not isinstance(result, Mapping) or result.get("identity") != self.identity:
364
+ raise DriverError(
365
+ "MODEL_IDENTITY_MISMATCH",
366
+ "SGLang immutable model identity mismatch",
367
+ consumed=True,
368
+ )
369
+ meta = result.get("meta_info")
370
+ if not isinstance(meta, Mapping):
371
+ raise DriverError(
372
+ "MODEL_RESPONSE_INVALID", "SGLang meta_info is required", consumed=True
373
+ )
374
+ finish = meta.get("finish_reason")
375
+ if not isinstance(finish, Mapping):
376
+ raise DriverError(
377
+ "MODEL_RESPONSE_INVALID", "SGLang finish_reason is required", consumed=True
378
+ )
379
+ return ModelTurn(
380
+ text=_bounded_text(result.get("text"), "model text"),
381
+ finish_reason=str(finish.get("type", "")),
382
+ matched=str(finish.get("matched", "")),
383
+ input_tokens=_nonnegative(meta.get("prompt_tokens"), "prompt_tokens"),
384
+ output_tokens=_nonnegative(meta.get("completion_tokens"), "completion_tokens"),
385
+ )
386
+ if self.mode != "openai_completions":
387
+ raise DriverError("MODEL_CONFIGURATION_INVALID", "unsupported model transport")
388
+ result = self.http.post(
389
+ self.base_url + "/v1/completions",
390
+ {
391
+ "model": self.model,
392
+ "prompt": prompt,
393
+ "temperature": sampling["temperature"],
394
+ "max_tokens": sampling["max_new_tokens"],
395
+ "stop": sampling["stop"],
396
+ },
397
+ )
398
+ if not isinstance(result, Mapping) or result.get("identity") != self.identity:
399
+ raise DriverError(
400
+ "MODEL_IDENTITY_MISMATCH",
401
+ "completion immutable model identity mismatch",
402
+ consumed=True,
403
+ )
404
+ choices = result.get("choices")
405
+ usage = result.get("usage")
406
+ if not isinstance(choices, Sequence) or not choices or not isinstance(choices[0], Mapping):
407
+ raise DriverError(
408
+ "MODEL_RESPONSE_INVALID", "completion choice is required", consumed=True
409
+ )
410
+ if not isinstance(usage, Mapping):
411
+ raise DriverError(
412
+ "MODEL_RESPONSE_INVALID", "completion usage is required", consumed=True
413
+ )
414
+ text = _bounded_text(choices[0].get("text"), "completion text")
415
+ text, matched = _completion_stop(text, choices[0])
416
+ return ModelTurn(
417
+ text=text,
418
+ finish_reason=str(choices[0].get("finish_reason", "")),
419
+ matched=matched,
420
+ input_tokens=_nonnegative(usage.get("prompt_tokens"), "prompt_tokens"),
421
+ output_tokens=_nonnegative(usage.get("completion_tokens"), "completion_tokens"),
422
+ )
423
+
424
+
425
+ class LocalSnapshotOpenAIModel:
426
+ """Use a local vLLM endpoint after matching it to the frozen model root."""
427
+
428
+ def __init__(self, config: Mapping[str, Any], *, api_key: str = "") -> None:
429
+ self.mode = _text(config, "model_transport")
430
+ if self.mode != LOCAL_MODEL_TRANSPORT:
431
+ raise DriverError("MODEL_CONFIGURATION_INVALID", "local model transport drifted")
432
+ self.base_url = _http_base(_text(config, "model_base_url"))
433
+ self.tokenizer_base_url = _http_base(_text(config, "tokenizer_base_url"))
434
+ self.model = _text(config, "served_model_name")
435
+ self.model_root = Path(_text(config, "local_model_root")).expanduser().resolve()
436
+ self.identity = {
437
+ key: config[key]
438
+ for key in (
439
+ "checkpoint_repository",
440
+ "checkpoint_revision",
441
+ "model_snapshot_tree_sha256",
442
+ "tokenizer_revision",
443
+ "served_model_name",
444
+ "context_admission_fingerprint_sha256",
445
+ )
446
+ }
447
+ self.http = _HTTPJSON(
448
+ api_key=api_key,
449
+ timeout=_positive_float(config, "model_timeout_seconds"),
450
+ )
451
+ self._verify_served_root()
452
+
453
+ def _verify_served_root(self) -> None:
454
+ request = urllib.request.Request(
455
+ self.base_url + "/v1/models",
456
+ headers={"Accept": "application/json"},
457
+ method="GET",
458
+ )
459
+ try:
460
+ with urllib.request.urlopen(request, timeout=self.http.timeout) as response:
461
+ raw = response.read(MAX_RESPONSE_BYTES + 1)
462
+ except Exception as exc:
463
+ raise DriverError(
464
+ "MODEL_IDENTITY_MISMATCH", "local vLLM model inventory is unavailable"
465
+ ) from exc
466
+ if len(raw) > MAX_RESPONSE_BYTES:
467
+ raise DriverError("MODEL_IDENTITY_MISMATCH", "model inventory is too large")
468
+ result = _json_object(raw, "model inventory")
469
+ rows = result.get("data")
470
+ if isinstance(rows, (str, bytes)) or not isinstance(rows, Sequence):
471
+ raise DriverError("MODEL_IDENTITY_MISMATCH", "model inventory is invalid")
472
+ matches = [
473
+ item for item in rows if isinstance(item, Mapping) and item.get("id") == self.model
474
+ ]
475
+ if len(matches) != 1:
476
+ raise DriverError("MODEL_IDENTITY_MISMATCH", "served model alias is not unique")
477
+ served_root = matches[0].get("root")
478
+ if not isinstance(served_root, str):
479
+ raise DriverError("MODEL_IDENTITY_MISMATCH", "served model root is absent")
480
+ if Path(served_root).expanduser().resolve() != self.model_root:
481
+ raise DriverError("MODEL_IDENTITY_MISMATCH", "served model root differs")
482
+
483
+ def fingerprint(self) -> Mapping[str, Any]:
484
+ return {
485
+ "transport": self.mode,
486
+ "base_url": self.base_url,
487
+ "tokenizer_base_url": self.tokenizer_base_url,
488
+ "identity": self.identity,
489
+ }
490
+
491
+ def count_prompt_tokens(self, prompt: str) -> int:
492
+ result = self.http.post(
493
+ self.tokenizer_base_url + "/tokenize",
494
+ {"model": self.model, "prompt": prompt},
495
+ )
496
+ if not isinstance(result, Mapping):
497
+ raise DriverError(
498
+ "CONTEXT_ADMISSION_FAILED", "local tokenizer response is invalid", consumed=True
499
+ )
500
+ return _nonnegative(result.get("count"), "admitted input_tokens")
501
+
502
+ def generate(self, prompt: str, sampling: Mapping[str, Any]) -> ModelTurn:
503
+ result = self.http.post(
504
+ self.base_url + "/v1/completions",
505
+ {
506
+ "model": self.model,
507
+ "prompt": prompt,
508
+ "temperature": sampling["temperature"],
509
+ "max_tokens": sampling["max_new_tokens"],
510
+ "stop": sampling["stop"],
511
+ },
512
+ )
513
+ choices = result.get("choices") if isinstance(result, Mapping) else None
514
+ usage = result.get("usage") if isinstance(result, Mapping) else None
515
+ if not isinstance(choices, Sequence) or not choices or not isinstance(choices[0], Mapping):
516
+ raise DriverError(
517
+ "MODEL_RESPONSE_INVALID", "completion choice is required", consumed=True
518
+ )
519
+ if not isinstance(usage, Mapping):
520
+ raise DriverError(
521
+ "MODEL_RESPONSE_INVALID", "completion usage is required", consumed=True
522
+ )
523
+ text = _bounded_text(choices[0].get("text"), "completion text")
524
+ text, matched = _completion_stop(text, choices[0])
525
+ return ModelTurn(
526
+ text=text,
527
+ finish_reason=str(choices[0].get("finish_reason", "")),
528
+ matched=matched,
529
+ input_tokens=_nonnegative(usage.get("prompt_tokens"), "prompt_tokens"),
530
+ output_tokens=_nonnegative(usage.get("completion_tokens"), "completion_tokens"),
531
+ )
532
+
533
+
534
+ class HTTPRetriever:
535
+ def __init__(self, config: Mapping[str, Any], *, api_key: str = "") -> None:
536
+ self.base_url = _http_base(_text(config, "retriever_base_url"))
537
+ self.http = _HTTPJSON(
538
+ api_key=api_key,
539
+ timeout=_positive_float(config, "retriever_timeout_seconds"),
540
+ )
541
+ self.identity = {
542
+ key: config[key]
543
+ for key in (
544
+ "retriever_revision",
545
+ "corpus_sha256",
546
+ "index_sha256",
547
+ "retrieval_config_sha256",
548
+ )
549
+ }
550
+
551
+ def fingerprint(self) -> Mapping[str, Any]:
552
+ return {
553
+ "transport": "controlled_flashrag_search_v1",
554
+ "base_url": self.base_url,
555
+ **self.identity,
556
+ }
557
+
558
+ def search(self, query: str, *, top_n: int) -> Sequence[Mapping[str, Any]]:
559
+ result = self.http.post(
560
+ self.base_url + "/search",
561
+ {"query": query, "top_n": top_n, "return_score": False},
562
+ )
563
+ if not isinstance(result, Mapping) or result.get("identity") != self.identity:
564
+ raise DriverError(
565
+ "RETRIEVER_IDENTITY_MISMATCH", "retriever identity mismatch", consumed=True
566
+ )
567
+ documents = result.get("documents")
568
+ if not isinstance(documents, Sequence) or isinstance(documents, (str, bytes)):
569
+ raise DriverError(
570
+ "RETRIEVER_RESPONSE_INVALID", "retriever documents are required", consumed=True
571
+ )
572
+ return documents
573
+
574
+
575
+ class LocalPreparedRetriever:
576
+ """Run the same checksum-locked production retriever inside the worker."""
577
+
578
+ def __init__(self, config: Mapping[str, Any]) -> None:
579
+ from agentinputlab.experiments.production_builders import (
580
+ PreparedCorpusRetrieverBuilder,
581
+ ProductionRetrieverBuildRequest,
582
+ retriever_product_sha256,
583
+ )
584
+
585
+ parameters = config.get("local_retriever_builder_parameters")
586
+ benchmark_id = config.get("local_retriever_benchmark_id")
587
+ model_slot = config.get("local_retriever_model_slot")
588
+ seed = config.get("local_retriever_seed")
589
+ if not isinstance(parameters, Mapping):
590
+ raise DriverError(
591
+ "RUNTIME_MANIFEST_INVALID",
592
+ "local retriever builder parameters are required",
593
+ )
594
+ if not isinstance(benchmark_id, str) or not benchmark_id.strip():
595
+ raise DriverError("RUNTIME_MANIFEST_INVALID", "local retriever benchmark is required")
596
+ if not isinstance(model_slot, str) or not model_slot.strip():
597
+ raise DriverError("RUNTIME_MANIFEST_INVALID", "local retriever model slot is required")
598
+ if isinstance(seed, bool) or not isinstance(seed, int) or seed < 0:
599
+ raise DriverError("RUNTIME_MANIFEST_INVALID", "local retriever seed is invalid")
600
+ builder = PreparedCorpusRetrieverBuilder()
601
+ blockers = tuple(builder.preflight(parameters))
602
+ if blockers:
603
+ raise DriverError(
604
+ "LOCAL_RETRIEVER_PREFLIGHT_FAILED",
605
+ "; ".join(str(item) for item in blockers),
606
+ )
607
+ try:
608
+ retriever = builder.build(
609
+ ProductionRetrieverBuildRequest(
610
+ benchmark_id=benchmark_id,
611
+ model_slot=model_slot,
612
+ seed=seed,
613
+ builder_parameters=parameters,
614
+ )
615
+ )
616
+ observed = retriever_product_sha256(retriever)
617
+ except DriverError:
618
+ raise
619
+ except Exception as exc:
620
+ raise DriverError(
621
+ "LOCAL_RETRIEVER_BUILD_FAILED",
622
+ "checksum-locked local retriever could not be built",
623
+ ) from exc
624
+ if observed != config.get("index_sha256"):
625
+ raise DriverError(
626
+ "RETRIEVER_IDENTITY_MISMATCH",
627
+ "local retriever product hash differs from the frozen index",
628
+ )
629
+ self.retriever = retriever
630
+ self.identity = {
631
+ key: config[key]
632
+ for key in (
633
+ "retriever_revision",
634
+ "corpus_sha256",
635
+ "index_sha256",
636
+ "retrieval_config_sha256",
637
+ )
638
+ }
639
+ self.builder_parameters_sha256 = _content_hash(parameters)
640
+
641
+ def fingerprint(self) -> Mapping[str, Any]:
642
+ return {
643
+ "transport": LOCAL_RETRIEVER_TRANSPORT,
644
+ "builder_parameters_sha256": self.builder_parameters_sha256,
645
+ **self.identity,
646
+ }
647
+
648
+ def search(self, query: str, *, top_n: int) -> Sequence[Mapping[str, Any]]:
649
+ try:
650
+ rows = self.retriever.retrieve(query, top_k=top_n)
651
+ except Exception as exc:
652
+ raise DriverError(
653
+ "RETRIEVER_REQUEST_FAILED", "local retriever failed", consumed=True
654
+ ) from exc
655
+ documents = []
656
+ for row in rows:
657
+ title = row.document.title.strip()
658
+ text = row.document.text.strip()
659
+ documents.append(
660
+ {
661
+ "id": row.document.doc_id,
662
+ "contents": f"Title: {title}\n{text}" if title else text,
663
+ "score": row.score,
664
+ }
665
+ )
666
+ return documents
667
+
668
+
669
+ class _UpstreamResponse:
670
+ def __init__(self, value: Mapping[str, Any]) -> None:
671
+ self.value = value
672
+
673
+ def json(self) -> Mapping[str, Any]:
674
+ return self.value
675
+
676
+
677
+ class _ModelRequestsShim:
678
+ def __init__(self, model: Any, gate: BudgetGate) -> None:
679
+ self.model = model
680
+ self.gate = gate
681
+
682
+ def post(self, url: str, json: Mapping[str, Any]) -> _UpstreamResponse:
683
+ if url != "agentinputlab://model/generate":
684
+ raise DriverError("UNEXPECTED_NETWORK_ROUTE", "upstream requested an unapproved URL")
685
+ if set(json) != {"text", "sampling_params"}:
686
+ raise DriverError("UPSTREAM_API_MISMATCH", "unexpected upstream model request")
687
+ sampling = json["sampling_params"]
688
+ expected = {
689
+ "temperature": 0.0,
690
+ "max_new_tokens": 8192,
691
+ "stop": ["</search>", "</answer>"],
692
+ }
693
+ if not isinstance(sampling, Mapping) or dict(sampling) != expected:
694
+ raise DriverError("UPSTREAM_API_MISMATCH", "upstream generation contract drifted")
695
+ prompt = _bounded_text(json["text"], "prompt")
696
+ admitted_input_tokens: int | None = None
697
+ if self.gate.caps["context_tokens"] is not None:
698
+ counter = getattr(self.model, "count_prompt_tokens", None)
699
+ if not callable(counter):
700
+ raise DriverError(
701
+ "CONTEXT_ADMISSION_REQUIRED",
702
+ "model adapter has no exact tokenizer admission interface",
703
+ )
704
+ try:
705
+ admitted_input_tokens = _nonnegative(counter(prompt), "admitted input_tokens")
706
+ except DriverError:
707
+ raise
708
+ except Exception as exc:
709
+ raise DriverError(
710
+ "CONTEXT_ADMISSION_FAILED",
711
+ "tokenizer admission failed before model execution",
712
+ ) from exc
713
+ self.gate.before_model(admitted_input_tokens)
714
+ try:
715
+ turn = self.model.generate(prompt, sampling)
716
+ except DriverError as exc:
717
+ exc.consumed = True
718
+ raise
719
+ except Exception as exc:
720
+ raise DriverError(
721
+ "MODEL_REQUEST_FAILED", "model request failed", consumed=True
722
+ ) from exc
723
+ if not isinstance(turn, ModelTurn):
724
+ raise DriverError(
725
+ "MODEL_RESPONSE_INVALID", "model adapter returned invalid data", consumed=True
726
+ )
727
+ self.gate.after_model(
728
+ turn.input_tokens,
729
+ turn.output_tokens,
730
+ admitted_input_tokens=admitted_input_tokens,
731
+ )
732
+ return _UpstreamResponse(
733
+ {
734
+ "text": turn.text,
735
+ "meta_info": {
736
+ "finish_reason": {"type": turn.finish_reason, "matched": turn.matched}
737
+ },
738
+ }
739
+ )
740
+
741
+
742
+ class SearchRecorder:
743
+ def __init__(self, retriever: Any, gate: BudgetGate, artifacts: Mapping[str, Any]) -> None:
744
+ self.retriever = retriever
745
+ self.gate = gate
746
+ self.artifacts = artifacts
747
+ self.trajectory: list[Mapping[str, Any]] = []
748
+ self.evidence: list[Mapping[str, Any]] = []
749
+ self._documents: dict[str, tuple[str, int]] = {}
750
+
751
+ def execute(self, tool_calls: Sequence[str]) -> list[str]:
752
+ if len(tool_calls) != 1:
753
+ raise DriverError(
754
+ "SEARCH_TAG_POLICY_VIOLATION",
755
+ "exactly one non-nested search tag is allowed per model turn",
756
+ consumed=bool(self.gate.usage.model_calls),
757
+ )
758
+ query = _bounded_text(tool_calls[0], "search query").strip()
759
+ if not query:
760
+ raise DriverError(
761
+ "SEARCH_QUERY_INVALID", "search query must not be empty", consumed=True
762
+ )
763
+ if len(self.trajectory) >= 10:
764
+ raise DriverError(
765
+ "SEARCH_BUDGET_EXHAUSTED", "search loop exceeded ten calls", consumed=True
766
+ )
767
+ if _PROTOCOL_TAG.search(query):
768
+ raise DriverError(
769
+ "SEARCH_TAG_POLICY_VIOLATION",
770
+ "nested protocol tags are forbidden in a search query",
771
+ consumed=True,
772
+ )
773
+ self.gate.before_retrieval()
774
+ try:
775
+ documents = self.retriever.search(query, top_n=5)
776
+ except DriverError as exc:
777
+ exc.consumed = True
778
+ raise
779
+ except Exception as exc:
780
+ raise DriverError(
781
+ "RETRIEVER_REQUEST_FAILED", "retriever request failed", consumed=True
782
+ ) from exc
783
+ if len(documents) > 5:
784
+ raise DriverError(
785
+ "RETRIEVER_RESPONSE_INVALID",
786
+ "retriever returned more than five documents",
787
+ consumed=True,
788
+ )
789
+ clean: list[dict[str, Any]] = []
790
+ ids: list[str] = []
791
+ for item in documents:
792
+ if not isinstance(item, Mapping):
793
+ raise DriverError(
794
+ "RETRIEVER_RESPONSE_INVALID", "document must be an object", consumed=True
795
+ )
796
+ unknown = set(item) - {"id", "contents", "score"}
797
+ if unknown:
798
+ raise DriverError(
799
+ "RETRIEVER_RESPONSE_INVALID", "document contains unknown fields", consumed=True
800
+ )
801
+ document_id = _bounded_text(item.get("id"), "document id").strip()
802
+ contents = _bounded_text(item.get("contents"), "document contents").strip()
803
+ if not document_id or not contents:
804
+ raise DriverError(
805
+ "RETRIEVER_RESPONSE_INVALID",
806
+ "document id/text must not be empty",
807
+ consumed=True,
808
+ )
809
+ if _PROTOCOL_TAG.search(contents):
810
+ raise DriverError(
811
+ "DOCUMENT_PROTOCOL_INJECTION", "document contains a protocol tag", consumed=True
812
+ )
813
+ score = item.get("score")
814
+ if score is not None and (
815
+ isinstance(score, bool) or not isinstance(score, (int, float))
816
+ ):
817
+ raise DriverError(
818
+ "RETRIEVER_RESPONSE_INVALID", "document score must be numeric", consumed=True
819
+ )
820
+ clean_item = {"id": document_id, "contents": contents, "score": score}
821
+ clean.append(clean_item)
822
+ ids.append(document_id)
823
+ previous = self._documents.get(document_id)
824
+ if previous is not None and previous[0] != contents:
825
+ raise DriverError(
826
+ "DOCUMENT_ID_COLLISION", "document ID changed content", consumed=True
827
+ )
828
+ if previous is None:
829
+ step = len(self.trajectory)
830
+ self._documents[document_id] = (contents, step)
831
+ self.evidence.append(
832
+ {
833
+ "document_id": document_id,
834
+ "text": contents,
835
+ "source": "smartsearch.wikipedia_search",
836
+ "score": score,
837
+ "first_seen_step": step,
838
+ "corpus_sha256": self.artifacts["corpus_sha256"],
839
+ "index_sha256": self.artifacts["index_sha256"],
840
+ "document_sha256": _content_hash(
841
+ {"document_id": document_id, "text": contents}
842
+ ),
843
+ "metadata": {},
844
+ }
845
+ )
846
+ if len(ids) != len(set(ids)):
847
+ raise DriverError(
848
+ "RETRIEVER_RESPONSE_INVALID",
849
+ "retriever returned duplicate document IDs",
850
+ consumed=True,
851
+ )
852
+ request_record = {"query": query, "top_n": 5, "return_score": False}
853
+ self.trajectory.append(
854
+ {
855
+ "index": len(self.trajectory),
856
+ "query": query,
857
+ "raw_search_text": query,
858
+ "raw_search_hash": _sha256(query.encode("utf-8")),
859
+ "tag_parse_status": "ok",
860
+ "document_ids": ids,
861
+ "retrieval_request_sha256": _content_hash(request_record),
862
+ "retrieval_response_sha256": _content_hash(clean),
863
+ }
864
+ )
865
+ text = "\n\n".join(item["contents"] for item in clean).strip()
866
+ return [f"result: \n{text}".strip()]
867
+
868
+
869
+ class SmartSearchDriver:
870
+ def __init__(
871
+ self,
872
+ *,
873
+ checkout: str | Path | None = None,
874
+ manifest_path: str | Path = DEFAULT_MANIFEST,
875
+ runtime_config: Mapping[str, Any] | None = None,
876
+ model_factory: Callable[[Mapping[str, Any]], Any] | None = None,
877
+ retriever_factory: Callable[[Mapping[str, Any]], Any] | None = None,
878
+ environ: Mapping[str, str] | None = None,
879
+ ) -> None:
880
+ self.environ = dict(os.environ if environ is None else environ)
881
+ if self.environ.get(SANDBOX_ENV):
882
+ raise DriverError(
883
+ "SANDBOX_FORBIDDEN", "the upstream code-execution sandbox is forbidden"
884
+ )
885
+ self.checkout = Path(checkout or self.environ.get(CHECKOUT_ENV, "")).expanduser()
886
+ if not str(self.checkout):
887
+ raise DriverError("CHECKOUT_REQUIRED", f"{CHECKOUT_ENV} is required")
888
+ self.manifest_path = Path(manifest_path)
889
+ self.manifest_bytes = self.manifest_path.read_bytes()
890
+ self.manifest_sha256 = _sha256(self.manifest_bytes)
891
+ self.manifest = _json_object(self.manifest_bytes, "compatibility manifest")
892
+ self.runtime = dict(runtime_config or self._runtime_from_environment())
893
+ self.source_attestation = self._verify_source()
894
+ self._verify_runtime_manifest()
895
+ self.recall_class, self.answer_parser = self._load_upstream_surface()
896
+ if model_factory is not None:
897
+ self.model_factory = model_factory
898
+ elif self.runtime.get("model_transport") == LOCAL_MODEL_TRANSPORT:
899
+ self.model_factory = self._local_model
900
+ else:
901
+ self.model_factory = self._http_model
902
+ if retriever_factory is not None:
903
+ self.retriever_factory = retriever_factory
904
+ elif self.runtime.get("retriever_transport") == LOCAL_RETRIEVER_TRANSPORT:
905
+ self.retriever_factory = self._local_retriever
906
+ else:
907
+ self.retriever_factory = self._http_retriever
908
+ self.last_usage: Usage | None = None
909
+
910
+ def _runtime_from_environment(self) -> Mapping[str, Any]:
911
+ path = self.environ.get(RUNTIME_MANIFEST_ENV, "")
912
+ if not path:
913
+ raise DriverError("RUNTIME_MANIFEST_REQUIRED", f"{RUNTIME_MANIFEST_ENV} is required")
914
+ return _json_object(Path(path).read_bytes(), "runtime manifest")
915
+
916
+ def _verify_source(self) -> str:
917
+ if self.manifest.get("schema_version") != MANIFEST_SCHEMA:
918
+ raise DriverError("MANIFEST_INVALID", "compatibility manifest schema mismatch")
919
+ upstream = self.manifest.get("upstream")
920
+ source_files = self.manifest.get("source_files")
921
+ if not isinstance(upstream, Mapping) or not isinstance(source_files, Mapping):
922
+ raise DriverError("MANIFEST_INVALID", "compatibility manifest is incomplete")
923
+ if upstream.get("commit") != UPSTREAM_COMMIT:
924
+ raise DriverError("MANIFEST_INVALID", "compatibility manifest commit mismatch")
925
+ checks = {"LICENSE": upstream.get("license_sha256"), **dict(source_files)}
926
+ for relative, expected in checks.items():
927
+ path = self.checkout / relative
928
+ if not path.is_file() or _sha256(path.read_bytes()) != expected:
929
+ raise DriverError("SOURCE_HASH_MISMATCH", f"pinned source mismatch: {relative}")
930
+ if (self.checkout / ".git").exists():
931
+ git_environment = {
932
+ "PATH": os.defpath,
933
+ "LANG": "C",
934
+ "LC_ALL": "C",
935
+ "GIT_CONFIG_NOSYSTEM": "1",
936
+ "GIT_CONFIG_GLOBAL": os.devnull,
937
+ "GIT_OPTIONAL_LOCKS": "0",
938
+ "GIT_TERMINAL_PROMPT": "0",
939
+ }
940
+ try:
941
+ head = subprocess.run(
942
+ [
943
+ "git",
944
+ "-c",
945
+ "core.fsmonitor=false",
946
+ "-c",
947
+ f"core.hooksPath={os.devnull}",
948
+ "rev-parse",
949
+ "HEAD",
950
+ ],
951
+ cwd=self.checkout,
952
+ check=True,
953
+ capture_output=True,
954
+ text=True,
955
+ timeout=10,
956
+ env=git_environment,
957
+ ).stdout.strip()
958
+ top_level = subprocess.run(
959
+ [
960
+ "git",
961
+ "-c",
962
+ "core.fsmonitor=false",
963
+ "-c",
964
+ f"core.hooksPath={os.devnull}",
965
+ "rev-parse",
966
+ "--show-toplevel",
967
+ ],
968
+ cwd=self.checkout,
969
+ check=True,
970
+ capture_output=True,
971
+ text=True,
972
+ timeout=10,
973
+ env=git_environment,
974
+ ).stdout.strip()
975
+ dirty = subprocess.run(
976
+ [
977
+ "git",
978
+ "-c",
979
+ "core.fsmonitor=false",
980
+ "-c",
981
+ f"core.hooksPath={os.devnull}",
982
+ "status",
983
+ "--porcelain",
984
+ "--untracked-files=all",
985
+ ],
986
+ cwd=self.checkout,
987
+ check=True,
988
+ capture_output=True,
989
+ text=True,
990
+ timeout=10,
991
+ env=git_environment,
992
+ ).stdout.strip()
993
+ except Exception as exc:
994
+ raise DriverError("GIT_ATTESTATION_FAILED", "could not attest checkout") from exc
995
+ if (
996
+ head != UPSTREAM_COMMIT
997
+ or Path(top_level).resolve() != self.checkout.resolve()
998
+ or dirty
999
+ ):
1000
+ raise DriverError("GIT_ATTESTATION_FAILED", "checkout is not pinned and clean")
1001
+ return "git_checkout"
1002
+ archive_path = self.runtime.get("source_archive_path") or self.environ.get(
1003
+ SOURCE_ARCHIVE_ENV
1004
+ )
1005
+ snapshot_commit = self.runtime.get("snapshot_commit") or self.environ.get(
1006
+ SNAPSHOT_COMMIT_ENV
1007
+ )
1008
+ if snapshot_commit != UPSTREAM_COMMIT or not isinstance(archive_path, str):
1009
+ raise DriverError(
1010
+ "SOURCE_ATTESTATION_REQUIRED", "snapshot archive attestation is required"
1011
+ )
1012
+ archive = Path(archive_path)
1013
+ if not archive.is_file() or _sha256(archive.read_bytes()) != upstream.get("archive_sha256"):
1014
+ raise DriverError("SOURCE_ARCHIVE_MISMATCH", "source archive hash mismatch")
1015
+ return "verified_archive_snapshot"
1016
+
1017
+ def _verify_runtime_manifest(self) -> None:
1018
+ artifacts = self.runtime.get("artifacts")
1019
+ if not isinstance(artifacts, Mapping):
1020
+ raise DriverError("RUNTIME_MANIFEST_INVALID", "runtime artifacts are required")
1021
+ if set(artifacts) != _ARTIFACT_FIELDS:
1022
+ raise DriverError("RUNTIME_MANIFEST_INVALID", "runtime artifact fields are not exact")
1023
+ _validate_artifact_digests(artifacts)
1024
+ if artifacts.get("compatibility_manifest_sha256") != self.manifest_sha256:
1025
+ raise DriverError("RUNTIME_MANIFEST_INVALID", "compatibility manifest hash mismatch")
1026
+ if (
1027
+ artifacts.get("checkpoint_repository") != CHECKPOINT_REPOSITORY
1028
+ or artifacts.get("checkpoint_revision") != CHECKPOINT_REVISION
1029
+ ):
1030
+ raise DriverError("RUNTIME_MANIFEST_INVALID", "released checkpoint identity mismatch")
1031
+ snapshot_path = self.runtime.get("model_snapshot_manifest_path")
1032
+ if not isinstance(snapshot_path, str) or not Path(snapshot_path).is_file():
1033
+ raise DriverError(
1034
+ "MODEL_SNAPSHOT_MANIFEST_REQUIRED", "model snapshot manifest is required"
1035
+ )
1036
+ raw = Path(snapshot_path).read_bytes()
1037
+ if _sha256(raw) != artifacts.get("model_snapshot_manifest_sha256"):
1038
+ raise DriverError("MODEL_SNAPSHOT_MISMATCH", "model snapshot manifest hash mismatch")
1039
+ snapshot = _json_object(raw, "model snapshot manifest")
1040
+ if (
1041
+ snapshot.get("repository") != CHECKPOINT_REPOSITORY
1042
+ or snapshot.get("revision") != CHECKPOINT_REVISION
1043
+ ):
1044
+ raise DriverError("MODEL_SNAPSHOT_MISMATCH", "model snapshot revision mismatch")
1045
+ if snapshot.get("tokenizer_revision") != artifacts.get("tokenizer_revision"):
1046
+ raise DriverError("MODEL_SNAPSHOT_MISMATCH", "tokenizer revision mismatch")
1047
+ files = snapshot.get("files")
1048
+ if not isinstance(files, Mapping) or not files:
1049
+ raise DriverError("MODEL_SNAPSHOT_MISMATCH", "model snapshot file manifest is empty")
1050
+ _validate_snapshot_files(files)
1051
+ snapshot_tree_sha256 = _content_hash(files)
1052
+ if (
1053
+ snapshot.get("snapshot_tree_sha256") != snapshot_tree_sha256
1054
+ or artifacts.get("model_snapshot_tree_sha256") != snapshot_tree_sha256
1055
+ ):
1056
+ raise DriverError("MODEL_SNAPSHOT_MISMATCH", "model snapshot tree mismatch")
1057
+
1058
+ contracts = self.manifest.get("contracts")
1059
+ if not isinstance(contracts, Mapping) or set(contracts) != _CONTRACT_FIELDS:
1060
+ raise DriverError("MANIFEST_INVALID", "compatibility contracts are not exact")
1061
+ for key in (
1062
+ "tool_schema_sha256",
1063
+ "loop_contract_sha256",
1064
+ "parser_contract_sha256",
1065
+ ):
1066
+ if contracts.get(key) != artifacts.get(key):
1067
+ raise DriverError("CONTRACT_MISMATCH", f"{key} disagrees with artifacts")
1068
+ fixed_contracts = {
1069
+ "tool_schema_sha256": TOOL_SCHEMA_SHA256,
1070
+ "loop_contract_sha256": LOOP_CONTRACT_SHA256,
1071
+ "parser_contract_sha256": PARSER_CONTRACT_SHA256,
1072
+ }
1073
+ if any(artifacts.get(key) != expected for key, expected in fixed_contracts.items()):
1074
+ raise DriverError("CONTRACT_MISMATCH", "frozen contract digest drifted")
1075
+ if contracts.get("profile_id") != PROFILE_ID:
1076
+ raise DriverError("CONTRACT_MISMATCH", "contract profile mismatch")
1077
+ expected_loop = {
1078
+ "max_rounds": contracts.get("max_rounds"),
1079
+ "max_results_per_search": contracts.get("top_n"),
1080
+ "max_new_tokens": contracts.get("max_new_tokens"),
1081
+ "temperature": contracts.get("temperature"),
1082
+ "stop": contracts.get("stop"),
1083
+ "retry_attempts": contracts.get("outer_retry_attempts"),
1084
+ "search_tags_per_turn": contracts.get("search_tags_per_turn"),
1085
+ }
1086
+ if _content_hash(expected_loop) != artifacts.get("loop_contract_sha256"):
1087
+ raise DriverError("CONTRACT_MISMATCH", "loop contract hash mismatch")
1088
+ tool_schema = self.runtime.get("tool_schema")
1089
+ if (
1090
+ isinstance(tool_schema, (str, bytes))
1091
+ or not isinstance(tool_schema, Sequence)
1092
+ or _content_hash(tool_schema) != artifacts.get("tool_schema_sha256")
1093
+ ):
1094
+ raise DriverError("CONTRACT_MISMATCH", "runtime tool schema hash mismatch")
1095
+ for key in (
1096
+ "model_transport",
1097
+ "served_model_name",
1098
+ "model_snapshot_tree_sha256",
1099
+ "tokenizer_revision",
1100
+ "context_admission_fingerprint_sha256",
1101
+ "retriever_revision",
1102
+ "corpus_sha256",
1103
+ "index_sha256",
1104
+ "retrieval_config_sha256",
1105
+ ):
1106
+ if self.runtime.get(key) != artifacts.get(key):
1107
+ raise DriverError(
1108
+ "RUNTIME_MANIFEST_INVALID", f"runtime/artifact identity mismatch: {key}"
1109
+ )
1110
+ if self.runtime.get("retriever_transport") == LOCAL_RETRIEVER_TRANSPORT:
1111
+ required = {
1112
+ "local_retriever_builder_parameters",
1113
+ "local_retriever_benchmark_id",
1114
+ "local_retriever_model_slot",
1115
+ "local_retriever_seed",
1116
+ }
1117
+ if any(key not in self.runtime for key in required):
1118
+ raise DriverError(
1119
+ "RUNTIME_MANIFEST_INVALID",
1120
+ "local retriever runtime binding is incomplete",
1121
+ )
1122
+ if self.runtime.get("model_transport") == LOCAL_MODEL_TRANSPORT:
1123
+ model_root_value = self.runtime.get("local_model_root")
1124
+ if not isinstance(model_root_value, str) or not model_root_value.strip():
1125
+ raise DriverError(
1126
+ "RUNTIME_MANIFEST_INVALID", "local SmartSearch model root is required"
1127
+ )
1128
+ model_root = Path(model_root_value).expanduser().resolve()
1129
+ if not model_root.is_dir() or model_root.is_symlink():
1130
+ raise DriverError(
1131
+ "MODEL_SNAPSHOT_MISMATCH", "local SmartSearch model root is invalid"
1132
+ )
1133
+ for relative, identity in files.items():
1134
+ file_path = (model_root / relative).resolve()
1135
+ try:
1136
+ file_path.relative_to(model_root)
1137
+ except ValueError as exc:
1138
+ raise DriverError(
1139
+ "MODEL_SNAPSHOT_MISMATCH", "snapshot file escapes model root"
1140
+ ) from exc
1141
+ if (
1142
+ not file_path.is_file()
1143
+ or file_path.is_symlink()
1144
+ or file_path.stat().st_size != identity.get("bytes")
1145
+ or _sha256_file(file_path) != identity.get("sha256")
1146
+ ):
1147
+ raise DriverError(
1148
+ "MODEL_SNAPSHOT_MISMATCH",
1149
+ f"local model file checksum mismatch: {relative}",
1150
+ )
1151
+
1152
+ def _load_upstream_surface(self) -> tuple[type[Any], Callable[[str], str]]:
1153
+ recall_path = self.checkout / "src/re_call/inference/re_call.py"
1154
+ module = types.ModuleType(f"_agentinputlab_smartsearch_{self.manifest_sha256[:12]}")
1155
+ module.__file__ = str(recall_path)
1156
+ requests_placeholder = _UnboundRequestsModule()
1157
+ default_import = builtins.__import__
1158
+
1159
+ def controlled_import(name, globals=None, locals=None, fromlist=(), level=0):
1160
+ if level == 0 and name == "requests":
1161
+ if fromlist:
1162
+ raise DriverError(
1163
+ "UPSTREAM_API_MISMATCH",
1164
+ "pinned SmartSearch changed its requests import contract",
1165
+ )
1166
+ return requests_placeholder
1167
+ return default_import(name, globals, locals, fromlist, level)
1168
+
1169
+ module.__dict__["__builtins__"] = {
1170
+ **vars(builtins),
1171
+ "__import__": controlled_import,
1172
+ }
1173
+ old_dont_write = sys.dont_write_bytecode
1174
+ old_prefix = sys.pycache_prefix
1175
+ try:
1176
+ sys.dont_write_bytecode = True
1177
+ sys.pycache_prefix = tempfile.mkdtemp(prefix="agentinputlab-smartsearch-pycache-")
1178
+ exec(compile(recall_path.read_bytes(), str(recall_path), "exec"), module.__dict__)
1179
+ finally:
1180
+ sys.dont_write_bytecode = old_dont_write
1181
+ sys.pycache_prefix = old_prefix
1182
+ if module.__dict__.get("requests") is not requests_placeholder:
1183
+ raise DriverError(
1184
+ "UPSTREAM_API_MISMATCH",
1185
+ "pinned SmartSearch did not bind the controlled requests import seam",
1186
+ )
1187
+ recall = module.__dict__.get("ReCall")
1188
+ if not isinstance(recall, type) or not hasattr(recall.run, "__wrapped__"):
1189
+ raise DriverError("UPSTREAM_API_MISMATCH", "pinned ReCall API is unavailable")
1190
+ active_path = self.checkout / "src/flashrag/pipeline/active_pipeline.py"
1191
+ helper_path = self.checkout / "src/verl/utils/reward_score/re_call.py"
1192
+ extractor = _load_ast_function(
1193
+ active_path, "extract_last_assistant_response", class_name="ReCallPipeline"
1194
+ )
1195
+ last_boxed = _load_ast_function(helper_path, "last_boxed_only_string")
1196
+ remove_boxed = _load_ast_function(helper_path, "remove_boxed")
1197
+
1198
+ def parse_answer(transcript: str) -> str:
1199
+ part = extractor(object(), transcript)
1200
+ if not isinstance(part, str) or not part:
1201
+ return ""
1202
+ try:
1203
+ boxed = last_boxed(part)
1204
+ if boxed is None:
1205
+ return ""
1206
+ answer = remove_boxed(boxed)
1207
+ except (AssertionError, TypeError):
1208
+ return ""
1209
+ return answer if isinstance(answer, str) else ""
1210
+
1211
+ self._upstream_module = module
1212
+ return recall, parse_answer
1213
+
1214
+ def _http_model(self, config: Mapping[str, Any]) -> HTTPModelTransport:
1215
+ merged = {**dict(config), **dict(self.runtime["artifacts"])}
1216
+ return HTTPModelTransport(merged, api_key=self.environ.get(MODEL_API_KEY_ENV, ""))
1217
+
1218
+ def _http_retriever(self, config: Mapping[str, Any]) -> HTTPRetriever:
1219
+ merged = {**dict(config), **dict(self.runtime["artifacts"])}
1220
+ return HTTPRetriever(merged, api_key=self.environ.get(RETRIEVER_API_KEY_ENV, ""))
1221
+
1222
+ def _local_model(self, config: Mapping[str, Any]) -> LocalSnapshotOpenAIModel:
1223
+ merged = {**dict(config), **dict(self.runtime["artifacts"])}
1224
+ return LocalSnapshotOpenAIModel(
1225
+ merged,
1226
+ api_key=self.environ.get(MODEL_API_KEY_ENV, ""),
1227
+ )
1228
+
1229
+ def _local_retriever(self, config: Mapping[str, Any]) -> LocalPreparedRetriever:
1230
+ merged = {**dict(config), **dict(self.runtime["artifacts"])}
1231
+ return LocalPreparedRetriever(merged)
1232
+
1233
+ def run(self, request: Any) -> Mapping[str, Any]:
1234
+ self.last_usage = None
1235
+ parsed = _validate_request(request)
1236
+ artifacts = parsed["payload"]["artifacts"]
1237
+ if _content_hash(artifacts) != parsed["dependency_fingerprints"].get("artifacts_sha256"):
1238
+ raise DriverError("ARTIFACT_IDENTITY_MISMATCH", "artifact dependency hash mismatch")
1239
+ if _content_hash(artifacts) != _content_hash(self.runtime["artifacts"]):
1240
+ raise DriverError("ARTIFACT_IDENTITY_MISMATCH", "request/runtime artifact mismatch")
1241
+ model = self.model_factory(self.runtime)
1242
+ retriever = self.retriever_factory(self.runtime)
1243
+ if _content_hash(model.fingerprint()) != artifacts.get(
1244
+ "model_deployment_fingerprint_sha256"
1245
+ ):
1246
+ raise DriverError("MODEL_IDENTITY_MISMATCH", "model deployment fingerprint mismatch")
1247
+ if _content_hash(retriever.fingerprint()) != artifacts.get(
1248
+ "retriever_deployment_fingerprint_sha256"
1249
+ ):
1250
+ raise DriverError(
1251
+ "RETRIEVER_IDENTITY_MISMATCH", "retriever deployment fingerprint mismatch"
1252
+ )
1253
+
1254
+ gate = BudgetGate(parsed["payload"]["remaining_budget"])
1255
+ self.last_usage = gate.usage
1256
+ recorder = SearchRecorder(retriever, gate, artifacts)
1257
+ upstream_module = self._upstream_module
1258
+ upstream_module.requests = _ModelRequestsShim(model, gate)
1259
+
1260
+ base = self.recall_class
1261
+
1262
+ class SafeReCall(base):
1263
+ def execute_tool_calls(self, env, tool_calls):
1264
+ if env != "agentinputlab:no-exec":
1265
+ raise DriverError("SANDBOX_FORBIDDEN", "dynamic tool environment is forbidden")
1266
+ return recorder.execute(tool_calls)
1267
+
1268
+ agent = SafeReCall("agentinputlab://model", "agentinputlab://disabled-executor")
1269
+ try:
1270
+ transcript = base.run.__wrapped__(
1271
+ agent,
1272
+ "agentinputlab:no-exec",
1273
+ _canonical_json(self.runtime.get("tool_schema", [])),
1274
+ parsed["payload"]["question"],
1275
+ )
1276
+ except DriverError:
1277
+ raise
1278
+ except Exception as exc:
1279
+ raise DriverError(
1280
+ "UPSTREAM_EXECUTION_FAILED",
1281
+ "pinned SmartSearch inference failed",
1282
+ consumed=bool(gate.usage.model_calls or gate.usage.retrieval_calls),
1283
+ ) from exc
1284
+ answer = self.answer_parser(transcript)
1285
+ if not answer.strip():
1286
+ raise DriverError(
1287
+ "NO_FINAL_ANSWER", "official parser found no boxed final answer", consumed=True
1288
+ )
1289
+ termination = "direct_answer" if not recorder.trajectory else "answer"
1290
+ if gate.usage.model_calls == 10 and len(recorder.trajectory) == 10:
1291
+ termination = "max_search_rounds"
1292
+ payload = {
1293
+ "profile_id": PROFILE_ID,
1294
+ "artifact_identity_sha256": _content_hash(artifacts),
1295
+ "artifacts": artifacts,
1296
+ "gold_answer_used": False,
1297
+ "test_time_reward_used": False,
1298
+ "external_query_refiner_used": False,
1299
+ "training_code_loaded": False,
1300
+ "trajectory": recorder.trajectory,
1301
+ "evidence": recorder.evidence,
1302
+ "final_answer": answer,
1303
+ "termination": termination,
1304
+ "raw_transcript_sha256": _sha256(transcript.encode("utf-8")),
1305
+ "runtime_attestation": {
1306
+ "source_commit": UPSTREAM_COMMIT,
1307
+ "source_attestation": self.source_attestation,
1308
+ "compatibility_manifest_sha256": self.manifest_sha256,
1309
+ "model_transport": artifacts["model_transport"],
1310
+ "model_snapshot_tree_sha256": artifacts["model_snapshot_tree_sha256"],
1311
+ "context_admission_fingerprint_sha256": artifacts[
1312
+ "context_admission_fingerprint_sha256"
1313
+ ],
1314
+ "contract_hashes_verified": True,
1315
+ "immutable_model_identity_handshake_required": True,
1316
+ "context_admission_before_generation": True,
1317
+ "official_loop_called": True,
1318
+ "outer_retry_disabled": True,
1319
+ "dangerous_sandbox_used": False,
1320
+ "training_code_loaded": False,
1321
+ "seed_applied": False,
1322
+ },
1323
+ }
1324
+ return {
1325
+ "status": "ok",
1326
+ "request_id": parsed["request_id"],
1327
+ "upstream_commit": UPSTREAM_COMMIT,
1328
+ "protocol_version": PROTOCOL,
1329
+ "payload": payload,
1330
+ "usage": gate.usage.as_mapping(),
1331
+ }
1332
+
1333
+
1334
+ def _validate_request(value: Any) -> Mapping[str, Any]:
1335
+ if not isinstance(value, Mapping) or set(value) != _TOP_FIELDS:
1336
+ raise DriverError("REQUEST_SCHEMA_INVALID", "request envelope fields are invalid")
1337
+ _reject_forbidden(value)
1338
+ if value.get("method_id") != "recipe.smartsearch" or value.get("release_id") != RELEASE:
1339
+ raise DriverError("REQUEST_IDENTITY_MISMATCH", "method/release mismatch")
1340
+ if value.get("upstream_commit") != UPSTREAM_COMMIT or value.get("protocol_version") != PROTOCOL:
1341
+ raise DriverError("REQUEST_IDENTITY_MISMATCH", "commit/protocol mismatch")
1342
+ identity = {key: value[key] for key in _TOP_FIELDS if key != "request_id"}
1343
+ if value.get("request_id") != _content_hash(identity):
1344
+ raise DriverError("REQUEST_IDENTITY_MISMATCH", "request_id mismatch")
1345
+ if value.get("minimum_reservation") != {"model_calls": 1}:
1346
+ raise DriverError("REQUEST_SCHEMA_INVALID", "minimum reservation must be one model call")
1347
+ payload = value.get("payload")
1348
+ if not isinstance(payload, Mapping) or set(payload) != _PAYLOAD_FIELDS:
1349
+ raise DriverError("REQUEST_SCHEMA_INVALID", "payload fields are invalid")
1350
+ if payload.get("profile_id") != PROFILE_ID:
1351
+ raise DriverError("REQUEST_IDENTITY_MISMATCH", "profile mismatch")
1352
+ _bounded_text(payload.get("question"), "question")
1353
+ if not isinstance(payload.get("task_id"), str):
1354
+ raise DriverError("REQUEST_SCHEMA_INVALID", "task_id must be text")
1355
+ seed = payload.get("seed")
1356
+ if isinstance(seed, bool) or not isinstance(seed, int) or seed < 0:
1357
+ raise DriverError("REQUEST_SCHEMA_INVALID", "seed must be non-negative")
1358
+ if payload.get("controls") != {
1359
+ "max_search_rounds": 10,
1360
+ "max_results_per_round": 5,
1361
+ "max_evidence_documents": 50,
1362
+ "max_output_tokens_per_call": 8192,
1363
+ }:
1364
+ raise DriverError("REQUEST_SCHEMA_INVALID", "SmartSearch controls mismatch")
1365
+ expected_contract = {
1366
+ "mode": "frozen_inference",
1367
+ "allow_training": False,
1368
+ "allow_gold_answer": False,
1369
+ "allow_test_time_reward": False,
1370
+ "allow_external_query_refiner": False,
1371
+ }
1372
+ if payload.get("inference_contract") != expected_contract:
1373
+ raise DriverError("REQUEST_SCHEMA_INVALID", "frozen inference contract mismatch")
1374
+ budget = payload.get("remaining_budget")
1375
+ if not isinstance(budget, Mapping) or set(budget) != {
1376
+ "model_calls",
1377
+ "retrieval_calls",
1378
+ "output_tokens",
1379
+ "context_tokens",
1380
+ }:
1381
+ raise DriverError("REQUEST_SCHEMA_INVALID", "remaining budget fields are invalid")
1382
+ for key in ("model_calls", "retrieval_calls", "output_tokens"):
1383
+ _nonnegative(budget.get(key), key)
1384
+ if budget.get("context_tokens") is not None:
1385
+ _nonnegative(budget.get("context_tokens"), "context_tokens")
1386
+ artifacts = payload.get("artifacts")
1387
+ if not isinstance(artifacts, Mapping) or set(artifacts) != _ARTIFACT_FIELDS:
1388
+ raise DriverError("REQUEST_SCHEMA_INVALID", "artifact fields are not exact")
1389
+ _validate_artifact_digests(artifacts)
1390
+ if not isinstance(value.get("dependency_fingerprints"), Mapping):
1391
+ raise DriverError("REQUEST_SCHEMA_INVALID", "dependency fingerprints must be an object")
1392
+ return value
1393
+
1394
+
1395
+ def _load_ast_function(
1396
+ path: Path, function_name: str, *, class_name: str = ""
1397
+ ) -> Callable[..., Any]:
1398
+ tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
1399
+ candidates: Sequence[ast.stmt] = tree.body
1400
+ if class_name:
1401
+ owner = next(
1402
+ (
1403
+ node
1404
+ for node in tree.body
1405
+ if isinstance(node, ast.ClassDef) and node.name == class_name
1406
+ ),
1407
+ None,
1408
+ )
1409
+ if owner is None:
1410
+ raise DriverError("UPSTREAM_API_MISMATCH", f"missing upstream class {class_name}")
1411
+ candidates = owner.body
1412
+ node = next(
1413
+ (
1414
+ node
1415
+ for node in candidates
1416
+ if isinstance(node, ast.FunctionDef) and node.name == function_name
1417
+ ),
1418
+ None,
1419
+ )
1420
+ if node is None:
1421
+ raise DriverError("UPSTREAM_API_MISMATCH", f"missing upstream parser {function_name}")
1422
+ isolated = ast.Module(body=[node], type_ignores=[])
1423
+ ast.fix_missing_locations(isolated)
1424
+ namespace: dict[str, Any] = {"re": re}
1425
+ exec(compile(isolated, str(path), "exec"), namespace)
1426
+ return namespace[function_name]
1427
+
1428
+
1429
+ def _reject_forbidden(value: Any) -> None:
1430
+ if isinstance(value, Mapping):
1431
+ for key, item in value.items():
1432
+ normalized = str(key).strip().lower().replace("-", "_")
1433
+ if normalized in _FORBIDDEN_KEYS:
1434
+ raise DriverError("FORBIDDEN_FIELD", f"forbidden request field: {key}")
1435
+ _reject_forbidden(item)
1436
+ elif isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
1437
+ for item in value:
1438
+ _reject_forbidden(item)
1439
+
1440
+
1441
+ def _infer_matched_stop(text: str) -> str:
1442
+ for marker in ("</search>", "</answer>"):
1443
+ if text.endswith(marker):
1444
+ return marker
1445
+ return ""
1446
+
1447
+
1448
+ def _completion_stop(text: str, choice: Mapping[str, Any]) -> tuple[str, str]:
1449
+ """Reconstruct the SGLang stop contract without duplicating provider text.
1450
+
1451
+ vLLM normally strips a matched stop string and reports it in stop_reason.
1452
+ Upstream ReCall appends that matched string itself. EOS IDs/unknown strings
1453
+ do not authorize inventing an answer/search closing tag.
1454
+ """
1455
+ if choice.get("finish_reason") != "stop":
1456
+ return text, ""
1457
+ suffix = _infer_matched_stop(text)
1458
+ reason = choice.get("stop_reason")
1459
+ matched = reason if reason in ("</search>", "</answer>") else suffix
1460
+ if matched and suffix and matched != suffix:
1461
+ raise DriverError(
1462
+ "MODEL_RESPONSE_INVALID", "conflicting completion stop identities", consumed=True
1463
+ )
1464
+ if matched and text.endswith(matched):
1465
+ text = text[: -len(matched)]
1466
+ return text, matched
1467
+
1468
+
1469
+ def _text(value: Mapping[str, Any], key: str) -> str:
1470
+ result = value.get(key)
1471
+ if not isinstance(result, str) or not result.strip():
1472
+ raise DriverError("RUNTIME_MANIFEST_INVALID", f"runtime {key} is required")
1473
+ return result.strip()
1474
+
1475
+
1476
+ def _bounded_text(value: Any, label: str) -> str:
1477
+ if not isinstance(value, str) or len(value) > MAX_TEXT_CHARS:
1478
+ raise DriverError("TEXT_INVALID", f"{label} must be bounded text")
1479
+ return value
1480
+
1481
+
1482
+ def _positive_float(value: Mapping[str, Any], key: str) -> float:
1483
+ result = value.get(key)
1484
+ if isinstance(result, bool) or not isinstance(result, (int, float)) or result <= 0:
1485
+ raise DriverError("RUNTIME_MANIFEST_INVALID", f"runtime {key} must be positive")
1486
+ return float(result)
1487
+
1488
+
1489
+ def _nonnegative(value: Any, label: str) -> int:
1490
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
1491
+ raise DriverError("VALUE_INVALID", f"{label} must be a non-negative integer")
1492
+ return value
1493
+
1494
+
1495
+ def _validate_artifact_digests(artifacts: Mapping[str, Any]) -> None:
1496
+ for key, value in artifacts.items():
1497
+ if key.endswith("_sha256") and (
1498
+ not isinstance(value, str)
1499
+ or len(value) != 64
1500
+ or any(character not in _HEX for character in value)
1501
+ ):
1502
+ raise DriverError("RUNTIME_MANIFEST_INVALID", f"invalid artifact digest: {key}")
1503
+
1504
+
1505
+ def _validate_snapshot_files(files: Mapping[str, Any]) -> None:
1506
+ for relative, identity in files.items():
1507
+ if (
1508
+ not isinstance(relative, str)
1509
+ or not relative
1510
+ or Path(relative).is_absolute()
1511
+ or ".." in Path(relative).parts
1512
+ or not isinstance(identity, Mapping)
1513
+ or set(identity) - {"sha256", "bytes"}
1514
+ ):
1515
+ raise DriverError("MODEL_SNAPSHOT_MISMATCH", "snapshot file entry is invalid")
1516
+ digest = identity.get("sha256")
1517
+ if (
1518
+ not isinstance(digest, str)
1519
+ or len(digest) != 64
1520
+ or any(character not in _HEX for character in digest)
1521
+ ):
1522
+ raise DriverError("MODEL_SNAPSHOT_MISMATCH", "snapshot file digest is invalid")
1523
+ if "bytes" in identity:
1524
+ _nonnegative(identity["bytes"], "snapshot file bytes")
1525
+
1526
+
1527
+ def _http_base(value: str) -> str:
1528
+ parsed = urllib.parse.urlsplit(value)
1529
+ if parsed.scheme not in {"http", "https"} or not parsed.netloc:
1530
+ raise DriverError("ENDPOINT_INVALID", "runtime endpoint must be HTTP(S)")
1531
+ if parsed.username or parsed.password or parsed.query or parsed.fragment:
1532
+ raise DriverError("ENDPOINT_INVALID", "runtime endpoint contains forbidden components")
1533
+ return urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, parsed.path.rstrip("/"), "", ""))
1534
+
1535
+
1536
+ def _json_object(raw: bytes, label: str) -> Mapping[str, Any]:
1537
+ try:
1538
+ value = _strict_json_loads(raw)
1539
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
1540
+ raise DriverError("JSON_INVALID", f"{label} is invalid JSON") from exc
1541
+ if not isinstance(value, Mapping):
1542
+ raise DriverError("JSON_INVALID", f"{label} must be an object")
1543
+ return value
1544
+
1545
+
1546
+ def _strict_json_loads(raw: str | bytes) -> Any:
1547
+ return json.loads(
1548
+ raw,
1549
+ object_pairs_hook=_no_duplicate_object,
1550
+ parse_constant=_reject_json_constant,
1551
+ )
1552
+
1553
+
1554
+ def _reject_json_constant(value: str) -> Any:
1555
+ raise DriverError("JSON_INVALID", f"non-finite JSON number is forbidden: {value}")
1556
+
1557
+
1558
+ def _no_duplicate_object(pairs: Sequence[tuple[str, Any]]) -> Mapping[str, Any]:
1559
+ result: dict[str, Any] = {}
1560
+ for key, value in pairs:
1561
+ if key in result:
1562
+ raise DriverError("JSON_INVALID", f"duplicate JSON key: {key}")
1563
+ result[key] = value
1564
+ return result
1565
+
1566
+
1567
+ def _canonical_json(value: Any) -> str:
1568
+ return json.dumps(
1569
+ value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False
1570
+ )
1571
+
1572
+
1573
+ def _content_hash(value: Any) -> str:
1574
+ return _sha256(_canonical_json(value).encode("utf-8"))
1575
+
1576
+
1577
+ def _sha256(value: bytes) -> str:
1578
+ return hashlib.sha256(value).hexdigest()
1579
+
1580
+
1581
+ def _sha256_file(path: Path) -> str:
1582
+ digest = hashlib.sha256()
1583
+ with path.open("rb") as handle:
1584
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
1585
+ digest.update(chunk)
1586
+ return digest.hexdigest()
1587
+
1588
+
1589
+ def _error_response(
1590
+ error: DriverError, request: Any, usage: Usage | None = None
1591
+ ) -> Mapping[str, Any]:
1592
+ response: dict[str, Any] = {
1593
+ "status": "error",
1594
+ "failure_code": error.code,
1595
+ "error_message": error.public_message,
1596
+ }
1597
+ if usage is None or (
1598
+ not error.consumed and not usage.model_calls and not usage.retrieval_calls
1599
+ ):
1600
+ response["consumed"] = False
1601
+ else:
1602
+ response["consumed"] = True
1603
+ response["usage"] = usage.as_mapping()
1604
+ if isinstance(request, Mapping):
1605
+ for key in ("request_id", "upstream_commit", "protocol_version"):
1606
+ if isinstance(request.get(key), str):
1607
+ response[key] = request[key]
1608
+ return response
1609
+
1610
+
1611
+ def serve_jsonl(driver: SmartSearchDriver, source: TextIO, sink: TextIO) -> int:
1612
+ for line in source:
1613
+ request: Any = None
1614
+ if len(line.encode("utf-8")) > MAX_REQUEST_BYTES:
1615
+ response = _error_response(DriverError("REQUEST_TOO_LARGE", "request too large"), None)
1616
+ else:
1617
+ try:
1618
+ request = _strict_json_loads(line)
1619
+ response = driver.run(request)
1620
+ except (json.JSONDecodeError, DriverError) as exc:
1621
+ error = (
1622
+ exc
1623
+ if isinstance(exc, DriverError)
1624
+ else DriverError("INVALID_JSON", "invalid JSON")
1625
+ )
1626
+ response = _error_response(error, request, driver.last_usage)
1627
+ except Exception:
1628
+ response = _error_response(
1629
+ DriverError("WORKER_FAILURE", "worker failed safely"),
1630
+ request,
1631
+ driver.last_usage,
1632
+ )
1633
+ sink.write(_canonical_json(response) + "\n")
1634
+ sink.flush()
1635
+ return 0
1636
+
1637
+
1638
+ def main() -> int:
1639
+ return serve_jsonl(SmartSearchDriver(), sys.stdin, sys.stdout)
1640
+
1641
+
1642
+ if __name__ == "__main__":
1643
+ raise SystemExit(main())