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,754 @@
1
+ """Transparent monitoring wrappers for runtime dependencies."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from collections.abc import Mapping
7
+ from contextlib import suppress
8
+ from dataclasses import dataclass, replace
9
+ from typing import Any
10
+
11
+ from agentinputlab.backends.llm.base import GenerationRequest, GenerationResponse, LLMBackend
12
+ from agentinputlab.backends.retrieval.base import RetrievalResult, Retriever
13
+ from agentinputlab.backends.vector import DenseEmbedder, VectorSearcher
14
+ from agentinputlab.connectors.tools.base import ToolCall, ToolConnector, ToolResult, ToolSpec
15
+ from agentinputlab.contracts.errors import (
16
+ AlgorithmOutputError,
17
+ BudgetExceededError,
18
+ ExternalDependencyError,
19
+ ValidationError,
20
+ )
21
+ from agentinputlab.contracts.lifecycle import InterventionContext
22
+ from agentinputlab.contracts.state import FrozenAgentState
23
+
24
+ from .budget import BudgetLedger, PricingCatalog
25
+ from .models import EventKind, ResourceReceipt, ResourceUsage
26
+ from .monitor import RunMonitor
27
+
28
+ _EXTERNAL_USAGE_INTEGER_FIELDS = (
29
+ "model_calls",
30
+ "retrieval_calls",
31
+ "tool_calls",
32
+ "embedding_calls",
33
+ "unpriced_model_calls",
34
+ "input_tokens",
35
+ "output_tokens",
36
+ )
37
+ _EXTERNAL_USAGE_FLOAT_FIELDS = (
38
+ "cpu_seconds",
39
+ "accelerator_seconds",
40
+ "cost_usd",
41
+ "compute_cost_usd",
42
+ )
43
+ _EXTERNAL_USAGE_REQUIRED_FIELDS = (
44
+ "model_calls",
45
+ "retrieval_calls",
46
+ "input_tokens",
47
+ "output_tokens",
48
+ )
49
+ _EXTERNAL_USAGE_FIELDS = frozenset((*_EXTERNAL_USAGE_INTEGER_FIELDS, *_EXTERNAL_USAGE_FLOAT_FIELDS))
50
+ _EXTERNAL_RESERVATION_FIELDS = frozenset(
51
+ ("model_calls", "retrieval_calls", "tool_calls", "embedding_calls")
52
+ )
53
+
54
+
55
+ def normalize_external_usage(envelope: object) -> ResourceUsage:
56
+ """Validate a common, lossless usage envelope for external executors.
57
+
58
+ Model and retrieval call counts plus both token directions are mandatory.
59
+ A model call without an explicit provider cost is conservatively marked
60
+ unpriced, which makes a configured cost budget fail closed.
61
+ """
62
+
63
+ if not isinstance(envelope, Mapping):
64
+ raise AlgorithmOutputError("external runtime response requires a usage mapping")
65
+ if any(not isinstance(key, str) for key in envelope):
66
+ raise AlgorithmOutputError("external runtime usage keys must be strings")
67
+ unknown = set(envelope) - _EXTERNAL_USAGE_FIELDS
68
+ if unknown:
69
+ raise AlgorithmOutputError(
70
+ "external runtime usage contains unsupported fields: " + ", ".join(sorted(unknown))
71
+ )
72
+ missing = [field for field in _EXTERNAL_USAGE_REQUIRED_FIELDS if field not in envelope]
73
+ if missing:
74
+ raise AlgorithmOutputError(
75
+ "external runtime usage lacks required fields: " + ", ".join(missing)
76
+ )
77
+
78
+ values = dict(envelope)
79
+ model_calls = values["model_calls"]
80
+ if isinstance(model_calls, bool) or not isinstance(model_calls, int) or model_calls < 0:
81
+ raise AlgorithmOutputError("external runtime model_calls must be a non-negative integer")
82
+ if "cost_usd" not in values:
83
+ if "unpriced_model_calls" in values and values["unpriced_model_calls"] != model_calls:
84
+ raise AlgorithmOutputError(
85
+ "external runtime usage without cost_usd must mark every model call unpriced"
86
+ )
87
+ values["unpriced_model_calls"] = model_calls
88
+ else:
89
+ values.setdefault("unpriced_model_calls", 0)
90
+ for field in _EXTERNAL_USAGE_INTEGER_FIELDS:
91
+ values.setdefault(field, 0)
92
+ for field in _EXTERNAL_USAGE_FLOAT_FIELDS:
93
+ values.setdefault(field, 0.0)
94
+ try:
95
+ usage = ResourceUsage(**values)
96
+ except ValidationError as exc:
97
+ raise AlgorithmOutputError(f"invalid external runtime usage: {exc}") from exc
98
+ if usage.unpriced_model_calls > usage.model_calls:
99
+ raise AlgorithmOutputError("unpriced_model_calls cannot exceed model_calls")
100
+ return usage
101
+
102
+
103
+ def external_usage_counters(usage: ResourceUsage) -> dict[str, int | float]:
104
+ """Return counters accepted by ``InterventionRunner`` trace reconciliation."""
105
+
106
+ if not isinstance(usage, ResourceUsage):
107
+ raise TypeError("usage must be a ResourceUsage")
108
+ return {
109
+ field: getattr(usage, field)
110
+ for field in (*_EXTERNAL_USAGE_INTEGER_FIELDS, *_EXTERNAL_USAGE_FLOAT_FIELDS)
111
+ }
112
+
113
+
114
+ def _request_reservation(request: object) -> ResourceUsage:
115
+ planner = getattr(request, "external_reservation", None)
116
+ if not callable(planner):
117
+ return ResourceUsage()
118
+ envelope = planner()
119
+ if not isinstance(envelope, Mapping):
120
+ raise ValidationError("external reservation plan must be a mapping")
121
+ if any(not isinstance(key, str) for key in envelope):
122
+ raise ValidationError("external reservation plan keys must be strings")
123
+ unknown = set(envelope) - _EXTERNAL_RESERVATION_FIELDS
124
+ if unknown:
125
+ raise ValidationError(
126
+ "external reservation plan contains unsupported fields: " + ", ".join(sorted(unknown))
127
+ )
128
+ try:
129
+ return ResourceUsage(**dict(envelope))
130
+ except ValidationError as exc:
131
+ raise ValidationError(f"invalid external reservation plan: {exc}") from exc
132
+
133
+
134
+ def _usage_delta(actual: ResourceUsage, reserved: ResourceUsage) -> ResourceUsage:
135
+ values: dict[str, int | float] = {}
136
+ for field in (*_EXTERNAL_USAGE_INTEGER_FIELDS, *_EXTERNAL_USAGE_FLOAT_FIELDS):
137
+ difference = getattr(actual, field) - getattr(reserved, field)
138
+ if difference < 0:
139
+ raise AlgorithmOutputError(
140
+ f"external runtime usage {field} is below its pre-execution reservation"
141
+ )
142
+ values[field] = difference
143
+ return ResourceUsage(**values)
144
+
145
+
146
+ def _raise_for_declared_external_failure(request: object, result: object) -> None:
147
+ hook = getattr(request, "raise_for_external_status", None)
148
+ if callable(hook):
149
+ hook(result)
150
+
151
+
152
+ def _response_usage(result: object) -> ResourceUsage:
153
+ return normalize_external_usage(_response_usage_envelope(result))
154
+
155
+
156
+ def _response_usage_envelope(result: object) -> object:
157
+ return result.get("usage") if isinstance(result, Mapping) else getattr(result, "usage", None)
158
+
159
+
160
+ @dataclass(slots=True)
161
+ class ObservedExternalRuntime:
162
+ """Monitor a fingerprinted worker/runtime injected through the run context."""
163
+
164
+ inner: Any
165
+ monitor: RunMonitor
166
+ ledger: BudgetLedger
167
+ run_id: str
168
+
169
+ @property
170
+ def worker_id(self) -> str:
171
+ return str(getattr(self.inner, "worker_id", "external-runtime"))
172
+
173
+ def fingerprint(self) -> Mapping[str, Any]:
174
+ fingerprint = getattr(self.inner, "fingerprint", None)
175
+ if not callable(fingerprint):
176
+ raise ValidationError("external runtime must expose fingerprint()")
177
+ identity = fingerprint()
178
+ if not isinstance(identity, Mapping) or not identity:
179
+ raise ValidationError("external runtime fingerprint must be a non-empty mapping")
180
+ return identity
181
+
182
+ def call(self, payload: Any) -> Any:
183
+ return self._execute("call", payload)
184
+
185
+ def call_controlled(self, request: Any) -> Any:
186
+ """Call a mapping-based worker while retaining request control hooks."""
187
+
188
+ serializer = getattr(request, "as_dict", None)
189
+ if not callable(serializer):
190
+ raise ValidationError("controlled external request must expose as_dict()")
191
+ return self._execute("call", request, delegated_request=serializer())
192
+
193
+ def invoke(self, payload: Any) -> Any:
194
+ return self._execute("invoke", payload)
195
+
196
+ def reformulate(self, request: Any) -> Any:
197
+ return self._execute("reformulate", request)
198
+
199
+ def select(self, request: Any) -> Any:
200
+ return self._execute("select", request)
201
+
202
+ def run(self, request: Any) -> Any:
203
+ return self._execute("run", request)
204
+
205
+ def _execute(
206
+ self,
207
+ operation: str,
208
+ request: Any,
209
+ *,
210
+ delegated_request: Any = None,
211
+ ) -> Any:
212
+ method = getattr(self.inner, operation, None)
213
+ if not callable(method):
214
+ raise ValidationError(f"external runtime does not implement {operation}()")
215
+ component = f"external_runtime.{operation}"
216
+ started = time.perf_counter_ns()
217
+ self.monitor.emit(
218
+ EventKind.STAGE_STARTED,
219
+ run_id=self.run_id,
220
+ component=component,
221
+ payload={"runtime_fingerprint": dict(self.fingerprint())},
222
+ )
223
+ reservation = _request_reservation(request)
224
+ if reservation != ResourceUsage():
225
+ try:
226
+ self.ledger.reserve_pending(reservation)
227
+ except BudgetExceededError:
228
+ self.monitor.emit(
229
+ EventKind.BUDGET_EXCEEDED,
230
+ run_id=self.run_id,
231
+ component=component,
232
+ )
233
+ raise
234
+ try:
235
+ result = method(request if delegated_request is None else delegated_request)
236
+ except Exception as exc:
237
+ self._record_resource_receipt(component=component)
238
+ self._settle_runtime_exception(reservation, component=component)
239
+ self.monitor.record_failure(
240
+ exc,
241
+ run_id=self.run_id,
242
+ category="external_runtime_error",
243
+ stage=component,
244
+ )
245
+ raise
246
+ self._record_resource_receipt(component=component)
247
+ try:
248
+ _raise_for_declared_external_failure(request, result)
249
+ except (ExternalDependencyError, AlgorithmOutputError) as exc:
250
+ # A declared model/algorithm failure can carry fully known actual
251
+ # usage. It is distinct from a malformed usage envelope: settle
252
+ # those tokens before propagating the original typed failure.
253
+ self._settle_declared_failure(result, reservation, component=component)
254
+ self.monitor.record_failure(
255
+ exc,
256
+ run_id=self.run_id,
257
+ category="external_runtime_error",
258
+ stage=component,
259
+ )
260
+ raise
261
+ try:
262
+ usage = _response_usage(result)
263
+ _usage_delta(usage, reservation)
264
+ except AlgorithmOutputError as exc:
265
+ # The external side effect already happened. Preserve a known
266
+ # minimum model call and unknown price before rejecting telemetry,
267
+ # without double-counting a call already reserved above.
268
+ minimum_actual = reservation + ResourceUsage(
269
+ model_calls=0 if reservation.model_calls else 1,
270
+ unpriced_model_calls=max(1, reservation.model_calls),
271
+ )
272
+ self.monitor.record_failure(
273
+ exc,
274
+ run_id=self.run_id,
275
+ category="external_usage_error",
276
+ stage=component,
277
+ )
278
+ self._settle_actual(minimum_actual, reservation, component=component)
279
+ raise
280
+ self._settle_actual(usage, reservation, component=component)
281
+ self.monitor.emit(
282
+ EventKind.STAGE_FINISHED,
283
+ run_id=self.run_id,
284
+ component=component,
285
+ payload={
286
+ "duration_ms": (time.perf_counter_ns() - started) / 1_000_000,
287
+ "usage": usage.as_dict(),
288
+ },
289
+ )
290
+ return result
291
+
292
+ def _record_resource_receipt(self, *, component: str) -> None:
293
+ receipt = getattr(self.inner, "last_resource_receipt", None)
294
+ if isinstance(receipt, ResourceReceipt):
295
+ self.monitor.record_resource_receipt(
296
+ receipt,
297
+ run_id=self.run_id,
298
+ component=component,
299
+ )
300
+
301
+ def _settle_actual(
302
+ self,
303
+ actual: ResourceUsage,
304
+ reservation: ResourceUsage,
305
+ *,
306
+ component: str,
307
+ ) -> None:
308
+ delta = _usage_delta(actual, reservation)
309
+ try:
310
+ self.ledger.charge_actual(delta)
311
+ except BudgetExceededError:
312
+ self.ledger.commit_pending(reservation)
313
+ self.monitor.record_usage(actual, run_id=self.run_id, component=component)
314
+ self.monitor.emit(
315
+ EventKind.BUDGET_EXCEEDED,
316
+ run_id=self.run_id,
317
+ component=component,
318
+ )
319
+ raise
320
+ self.ledger.commit_pending(reservation)
321
+ self.monitor.record_usage(actual, run_id=self.run_id, component=component)
322
+
323
+ def _settle_declared_failure(
324
+ self,
325
+ result: object,
326
+ reservation: ResourceUsage,
327
+ *,
328
+ component: str,
329
+ ) -> None:
330
+ envelope = _response_usage_envelope(result)
331
+ explicitly_unconsumed = (
332
+ isinstance(result, Mapping) and result.get("consumed") is False and envelope is None
333
+ )
334
+ if explicitly_unconsumed:
335
+ self._cancel_pending(reservation)
336
+ return
337
+ try:
338
+ actual = normalize_external_usage(envelope)
339
+ except AlgorithmOutputError:
340
+ self._commit_failed_minimum(reservation, component=component)
341
+ return
342
+ self._cancel_pending(reservation)
343
+ try:
344
+ self.ledger.charge_actual(actual)
345
+ except BudgetExceededError:
346
+ # The typed worker failure remains the primary cell-invalidating
347
+ # exception, while the irreversible overage remains on the ledger.
348
+ self.monitor.emit(
349
+ EventKind.BUDGET_EXCEEDED,
350
+ run_id=self.run_id,
351
+ component=component,
352
+ )
353
+ self.monitor.record_usage(actual, run_id=self.run_id, component=component)
354
+
355
+ def _commit_failed_minimum(
356
+ self,
357
+ reservation: ResourceUsage,
358
+ *,
359
+ component: str,
360
+ ) -> None:
361
+ """Retain the reserved lower bound unless zero consumption was explicit."""
362
+
363
+ if reservation == ResourceUsage():
364
+ return
365
+ minimum_actual = reservation + ResourceUsage(unpriced_model_calls=reservation.model_calls)
366
+ # The declared worker failure remains the primary exception. The
367
+ # reservation was accepted before the side effect and is therefore the
368
+ # only trustworthy lower bound when failure telemetry is absent/broken.
369
+ with suppress(BudgetExceededError):
370
+ self._settle_actual(minimum_actual, reservation, component=component)
371
+
372
+ def _settle_runtime_exception(
373
+ self,
374
+ reservation: ResourceUsage,
375
+ *,
376
+ component: str,
377
+ ) -> None:
378
+ if reservation == ResourceUsage():
379
+ return
380
+ minimum_actual = reservation + ResourceUsage(unpriced_model_calls=reservation.model_calls)
381
+ # Preserve the runtime exception as the primary failure. Any budget
382
+ # overage remains sticky and the usage is already recorded.
383
+ with suppress(BudgetExceededError):
384
+ self._settle_actual(minimum_actual, reservation, component=component)
385
+
386
+ def _cancel_pending(self, reservation: ResourceUsage) -> None:
387
+ if reservation != ResourceUsage():
388
+ self.ledger.cancel_pending(reservation)
389
+
390
+
391
+ @dataclass(slots=True)
392
+ class ObservedLLMBackend:
393
+ inner: LLMBackend
394
+ monitor: RunMonitor
395
+ ledger: BudgetLedger
396
+ run_id: str
397
+ pricing: PricingCatalog | None = None
398
+
399
+ @property
400
+ def model_id(self) -> str:
401
+ return self.inner.model_id
402
+
403
+ @property
404
+ def capabilities(self) -> Any:
405
+ return self.inner.capabilities
406
+
407
+ def fingerprint(self) -> Mapping[str, Any]:
408
+ return self.inner.fingerprint()
409
+
410
+ def generate(self, request: GenerationRequest) -> GenerationResponse:
411
+ started = time.perf_counter_ns()
412
+ self.monitor.emit(
413
+ EventKind.STAGE_STARTED,
414
+ run_id=self.run_id,
415
+ component="llm",
416
+ payload={"model_id": self.model_id, "request_hash": request.request_hash},
417
+ )
418
+ call_usage = ResourceUsage(model_calls=1)
419
+ try:
420
+ self.ledger.reserve(call_usage)
421
+ except BudgetExceededError:
422
+ self.monitor.emit(EventKind.BUDGET_EXCEEDED, run_id=self.run_id, component="llm")
423
+ raise
424
+ self.monitor.record_usage(call_usage, run_id=self.run_id, component="llm")
425
+ try:
426
+ response = self.inner.generate(request)
427
+ except Exception as exc:
428
+ # An invocation without a usable response still counts as an
429
+ # attempt. Its token/cost consumption is unknown, even if a price
430
+ # table exists; do not silently report the failed call as free.
431
+ unknown_usage = ResourceUsage(unpriced_model_calls=1)
432
+ try:
433
+ self.ledger.charge_actual(unknown_usage)
434
+ except BudgetExceededError:
435
+ self.monitor.emit(EventKind.BUDGET_EXCEEDED, run_id=self.run_id, component="llm")
436
+ self.monitor.record_usage(unknown_usage, run_id=self.run_id, component="llm")
437
+ self.monitor.emit(
438
+ EventKind.STAGE_FINISHED,
439
+ run_id=self.run_id,
440
+ component="llm",
441
+ payload={"status": "failed", "usage_status": "unknown",
442
+ "token_counters": "known_lower_bound_only"},
443
+ )
444
+ self.monitor.record_failure(
445
+ exc,
446
+ run_id=self.run_id,
447
+ category="backend_error",
448
+ stage="llm",
449
+ )
450
+ raise
451
+ cost = response.usage.cost_usd
452
+ unpriced_calls = 0
453
+ if cost is None:
454
+ catalog = self.pricing or PricingCatalog()
455
+ if catalog.has_price(self.model_id):
456
+ cost = catalog.estimate(
457
+ self.model_id,
458
+ input_tokens=response.usage.input_tokens,
459
+ output_tokens=response.usage.output_tokens,
460
+ )
461
+ else:
462
+ cost = 0.0
463
+ unpriced_calls = 1
464
+ usage = ResourceUsage(
465
+ unpriced_model_calls=unpriced_calls,
466
+ input_tokens=response.usage.input_tokens,
467
+ output_tokens=response.usage.output_tokens,
468
+ cost_usd=cost,
469
+ )
470
+ try:
471
+ self.ledger.charge_actual(usage)
472
+ except BudgetExceededError:
473
+ # The provider has already returned, so its usage is irreversible
474
+ # and must remain visible even though the budget gate now fails.
475
+ self.monitor.record_usage(usage, run_id=self.run_id, component="llm")
476
+ self.monitor.emit(EventKind.BUDGET_EXCEEDED, run_id=self.run_id, component="llm")
477
+ raise
478
+ self.monitor.record_usage(usage, run_id=self.run_id, component="llm")
479
+ self.monitor.emit(
480
+ EventKind.STAGE_FINISHED,
481
+ run_id=self.run_id,
482
+ component="llm",
483
+ payload={
484
+ "duration_ms": (time.perf_counter_ns() - started) / 1_000_000,
485
+ "finish_reason": response.finish_reason,
486
+ },
487
+ )
488
+ return response
489
+
490
+
491
+ @dataclass(slots=True)
492
+ class ObservedRetriever:
493
+ inner: Retriever
494
+ monitor: RunMonitor
495
+ ledger: BudgetLedger
496
+ run_id: str
497
+
498
+ @property
499
+ def retriever_id(self) -> str:
500
+ return self.inner.retriever_id
501
+
502
+ def fingerprint(self) -> Mapping[str, Any]:
503
+ return self.inner.fingerprint()
504
+
505
+ def retrieve(self, query: str, *, top_k: int = 5) -> tuple[RetrievalResult, ...]:
506
+ started = time.perf_counter_ns()
507
+ self.monitor.emit(
508
+ EventKind.STAGE_STARTED,
509
+ run_id=self.run_id,
510
+ component="retriever",
511
+ payload={"top_k": top_k},
512
+ )
513
+ usage = ResourceUsage(retrieval_calls=1)
514
+ try:
515
+ self.ledger.reserve(usage)
516
+ except BudgetExceededError:
517
+ self.monitor.emit(
518
+ EventKind.BUDGET_EXCEEDED,
519
+ run_id=self.run_id,
520
+ component="retriever",
521
+ )
522
+ raise
523
+ self.monitor.record_usage(usage, run_id=self.run_id, component="retriever")
524
+ try:
525
+ results = self.inner.retrieve(query, top_k=top_k)
526
+ except Exception as exc:
527
+ self.monitor.record_failure(
528
+ exc,
529
+ run_id=self.run_id,
530
+ category="retrieval_error",
531
+ stage="retriever",
532
+ )
533
+ raise
534
+ self.monitor.emit(
535
+ EventKind.STAGE_FINISHED,
536
+ run_id=self.run_id,
537
+ component="retriever",
538
+ payload={
539
+ "duration_ms": (time.perf_counter_ns() - started) / 1_000_000,
540
+ "result_count": len(results),
541
+ },
542
+ )
543
+ return results
544
+
545
+
546
+ @dataclass(slots=True)
547
+ class ObservedDenseEmbedder:
548
+ inner: DenseEmbedder
549
+ monitor: RunMonitor
550
+ ledger: BudgetLedger
551
+ run_id: str
552
+
553
+ @property
554
+ def embedder_id(self) -> str:
555
+ return self.inner.embedder_id
556
+
557
+ def fingerprint(self) -> Mapping[str, Any]:
558
+ return self.inner.fingerprint()
559
+
560
+ def embed(self, texts: Any) -> Any:
561
+ started = time.perf_counter_ns()
562
+ self.monitor.emit(
563
+ EventKind.STAGE_STARTED,
564
+ run_id=self.run_id,
565
+ component="embedding",
566
+ payload={"input_count": len(texts)},
567
+ )
568
+ usage = ResourceUsage(embedding_calls=1)
569
+ self.ledger.reserve(usage)
570
+ self.monitor.record_usage(usage, run_id=self.run_id, component="embedding")
571
+ try:
572
+ vectors = self.inner.embed(texts)
573
+ except Exception as exc:
574
+ self.monitor.record_failure(
575
+ exc,
576
+ run_id=self.run_id,
577
+ category="embedding_error",
578
+ stage="embedding",
579
+ )
580
+ raise
581
+ self.monitor.emit(
582
+ EventKind.STAGE_FINISHED,
583
+ run_id=self.run_id,
584
+ component="embedding",
585
+ payload={
586
+ "duration_ms": (time.perf_counter_ns() - started) / 1_000_000,
587
+ "vector_count": len(vectors),
588
+ },
589
+ )
590
+ return vectors
591
+
592
+
593
+ @dataclass(slots=True)
594
+ class ObservedVectorSearcher:
595
+ inner: VectorSearcher
596
+ monitor: RunMonitor
597
+ ledger: BudgetLedger
598
+ run_id: str
599
+
600
+ @property
601
+ def searcher_id(self) -> str:
602
+ return self.inner.searcher_id
603
+
604
+ def fingerprint(self) -> Mapping[str, Any]:
605
+ return self.inner.fingerprint()
606
+
607
+ def search(self, vector: Any, *, top_k: int) -> Any:
608
+ started = time.perf_counter_ns()
609
+ self.monitor.emit(
610
+ EventKind.STAGE_STARTED,
611
+ run_id=self.run_id,
612
+ component="vector_search",
613
+ payload={"top_k": top_k},
614
+ )
615
+ usage = ResourceUsage(retrieval_calls=1)
616
+ try:
617
+ self.ledger.reserve(usage)
618
+ except BudgetExceededError:
619
+ self.monitor.emit(
620
+ EventKind.BUDGET_EXCEEDED,
621
+ run_id=self.run_id,
622
+ component="vector_search",
623
+ )
624
+ raise
625
+ self.monitor.record_usage(usage, run_id=self.run_id, component="vector_search")
626
+ try:
627
+ results = self.inner.search(vector, top_k=top_k)
628
+ except Exception as exc:
629
+ self.monitor.record_failure(
630
+ exc,
631
+ run_id=self.run_id,
632
+ category="retrieval_error",
633
+ stage="vector_search",
634
+ )
635
+ raise
636
+ self.monitor.emit(
637
+ EventKind.STAGE_FINISHED,
638
+ run_id=self.run_id,
639
+ component="vector_search",
640
+ payload={
641
+ "duration_ms": (time.perf_counter_ns() - started) / 1_000_000,
642
+ "result_count": len(results),
643
+ },
644
+ )
645
+ return results
646
+
647
+
648
+ @dataclass(slots=True)
649
+ class ObservedToolConnector:
650
+ inner: ToolConnector
651
+ monitor: RunMonitor
652
+ ledger: BudgetLedger
653
+ run_id: str
654
+
655
+ def list_tools(self) -> tuple[ToolSpec, ...]:
656
+ return self.inner.list_tools()
657
+
658
+ def fingerprint(self) -> Mapping[str, Any]:
659
+ return self.inner.fingerprint()
660
+
661
+ def call(self, request: ToolCall) -> ToolResult:
662
+ started = time.perf_counter_ns()
663
+ self.monitor.emit(
664
+ EventKind.STAGE_STARTED,
665
+ run_id=self.run_id,
666
+ component="tool",
667
+ payload={"tool_name": request.name, "call_id": request.call_id},
668
+ )
669
+ usage = ResourceUsage(tool_calls=1)
670
+ try:
671
+ self.ledger.reserve(usage)
672
+ except BudgetExceededError:
673
+ self.monitor.emit(EventKind.BUDGET_EXCEEDED, run_id=self.run_id, component="tool")
674
+ raise
675
+ self.monitor.record_usage(usage, run_id=self.run_id, component="tool")
676
+ try:
677
+ result = self.inner.call(request)
678
+ except Exception as exc:
679
+ self.monitor.record_failure(
680
+ exc,
681
+ run_id=self.run_id,
682
+ category="tool_error",
683
+ stage="tool",
684
+ )
685
+ raise
686
+ self.monitor.emit(
687
+ EventKind.STAGE_FINISHED,
688
+ run_id=self.run_id,
689
+ component="tool",
690
+ payload={
691
+ "duration_ms": (time.perf_counter_ns() - started) / 1_000_000,
692
+ "error": bool(result.error),
693
+ },
694
+ )
695
+ return result
696
+
697
+
698
+ def instrument_context(
699
+ context: InterventionContext,
700
+ state: FrozenAgentState,
701
+ monitor: RunMonitor,
702
+ *,
703
+ run_id: str,
704
+ pricing: PricingCatalog | None = None,
705
+ ) -> InterventionContext:
706
+ """Return a per-run context with transparent runtime accounting wrappers."""
707
+
708
+ ledger = context.budget_ledger or BudgetLedger(state.budget)
709
+ if not isinstance(ledger, BudgetLedger):
710
+ raise TypeError("context.budget_ledger must be a BudgetLedger")
711
+ if ledger.budget != state.budget:
712
+ raise ValueError("context budget ledger does not match the frozen state budget")
713
+ ledger.claim_for_run(run_id)
714
+ backend = context.backend
715
+ if isinstance(backend, ObservedLLMBackend):
716
+ backend = backend.inner
717
+ if backend is not None:
718
+ backend = ObservedLLMBackend(backend, monitor, ledger, run_id, pricing)
719
+ dense_embedder = context.dense_embedder
720
+ if isinstance(dense_embedder, ObservedDenseEmbedder):
721
+ dense_embedder = dense_embedder.inner
722
+ if dense_embedder is not None:
723
+ dense_embedder = ObservedDenseEmbedder(dense_embedder, monitor, ledger, run_id)
724
+ retriever = context.retriever
725
+ if isinstance(retriever, ObservedRetriever):
726
+ retriever = retriever.inner
727
+ if retriever is not None:
728
+ retriever = ObservedRetriever(retriever, monitor, ledger, run_id)
729
+ vector_searcher = context.vector_searcher
730
+ if isinstance(vector_searcher, ObservedVectorSearcher):
731
+ vector_searcher = vector_searcher.inner
732
+ if vector_searcher is not None:
733
+ vector_searcher = ObservedVectorSearcher(vector_searcher, monitor, ledger, run_id)
734
+ tools = context.tools
735
+ if isinstance(tools, ObservedToolConnector):
736
+ tools = tools.inner
737
+ if tools is not None:
738
+ tools = ObservedToolConnector(tools, monitor, ledger, run_id)
739
+ external_runtime = context.external_runtime
740
+ if isinstance(external_runtime, ObservedExternalRuntime):
741
+ external_runtime = external_runtime.inner
742
+ if external_runtime is not None:
743
+ external_runtime = ObservedExternalRuntime(external_runtime, monitor, ledger, run_id)
744
+ return replace(
745
+ context,
746
+ backend=backend,
747
+ dense_embedder=dense_embedder,
748
+ retriever=retriever,
749
+ vector_searcher=vector_searcher,
750
+ tools=tools,
751
+ external_runtime=external_runtime,
752
+ budget_ledger=ledger,
753
+ monitor=monitor,
754
+ )