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,285 @@
1
+ """Minimal intervention runner preserving paired state and typed failures."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from collections.abc import Mapping
7
+ from dataclasses import dataclass, field
8
+
9
+ from agentinputlab.contracts.enums import PatchOp, StrEnum
10
+ from agentinputlab.contracts.errors import (
11
+ AlgorithmOutputError,
12
+ BudgetExceededError,
13
+ IntentViolationError,
14
+ ValidationError,
15
+ )
16
+ from agentinputlab.contracts.intent import IntentCheck, check_intent_contract
17
+ from agentinputlab.contracts.lifecycle import CompositeResult, Intervention, InterventionContext
18
+ from agentinputlab.contracts.patch import InputPatch
19
+ from agentinputlab.contracts.state import FrozenAgentState
20
+ from agentinputlab.observability import (
21
+ BudgetLedger,
22
+ EventKind,
23
+ ObservedRunStatus,
24
+ PricingCatalog,
25
+ ResourceUsage,
26
+ RunMonitor,
27
+ instrument_context,
28
+ )
29
+
30
+ from .runtime_guard import runtime_dependency_nodes
31
+
32
+
33
+ class RunStatus(StrEnum):
34
+ SUCCESS = "success"
35
+ NO_OP = "no_op"
36
+ FALLBACK = "fallback"
37
+ BUDGET_EXHAUSTED = "budget_exhausted"
38
+
39
+
40
+ @dataclass(frozen=True, slots=True)
41
+ class RunRecord:
42
+ release_id: str
43
+ input_state_hash: str
44
+ output_state_hash: str
45
+ patch: InputPatch
46
+ status: RunStatus
47
+ monitor_run_id: str = ""
48
+ duration_ms: float | None = None
49
+ usage: ResourceUsage = field(default_factory=ResourceUsage)
50
+ intent_check: IntentCheck | None = None
51
+
52
+
53
+ class InterventionRunner:
54
+ """Run a profile without swallowing unavailable/error conditions."""
55
+
56
+ def __init__(self, *, monitor: RunMonitor | None = None) -> None:
57
+ self.monitor = monitor or RunMonitor()
58
+
59
+ def run(
60
+ self,
61
+ intervention: Intervention,
62
+ state: FrozenAgentState,
63
+ context: InterventionContext,
64
+ ) -> RunRecord:
65
+ _reject_hidden_runtime_dependencies(intervention)
66
+ monitor = context.monitor or self.monitor
67
+ if not isinstance(monitor, RunMonitor):
68
+ raise TypeError("context.monitor must be a RunMonitor")
69
+ intent_check: IntentCheck | None = None
70
+ with monitor.run_scope(
71
+ release_id=intervention.release_id,
72
+ state_hash=state.state_hash,
73
+ tags={"method_id": intervention.method_id, "family": intervention.family.value},
74
+ ) as run_id:
75
+ pricing = context.pricing_catalog
76
+ if pricing is not None and not isinstance(pricing, PricingCatalog):
77
+ raise TypeError("context.pricing_catalog must be a PricingCatalog")
78
+ runtime_context = instrument_context(
79
+ context,
80
+ state,
81
+ monitor,
82
+ run_id=run_id,
83
+ pricing=pricing,
84
+ )
85
+ ledger = runtime_context.budget_ledger
86
+ if not isinstance(ledger, BudgetLedger): # pragma: no cover - helper invariant
87
+ raise TypeError("instrumented context did not produce a BudgetLedger")
88
+ monitor.emit(EventKind.STAGE_STARTED, component="intervention")
89
+ try:
90
+ patch = intervention.propose(state, runtime_context)
91
+ if not isinstance(patch, InputPatch):
92
+ raise AlgorithmOutputError(
93
+ f"intervention must return an InputPatch; got {type(patch).__name__}"
94
+ )
95
+ if patch.method_id != intervention.method_id:
96
+ raise AlgorithmOutputError("intervention returned a patch for another method")
97
+ if patch.release_id != intervention.release_id:
98
+ raise AlgorithmOutputError("intervention returned a patch for another release")
99
+ self._reconcile_trace_usage(patch, ledger, monitor)
100
+ output = patch.apply(state)
101
+ if runtime_context.intent_contract is not None:
102
+ intent_check = check_intent_contract(
103
+ runtime_context.intent_contract,
104
+ state,
105
+ patch,
106
+ output,
107
+ )
108
+ monitor.emit(
109
+ EventKind.INTENT_CHECKED,
110
+ component="intent_gate",
111
+ payload={
112
+ "preserved": intent_check.preserved,
113
+ "missing_entities": intent_check.missing_entities,
114
+ "missing_constraints": intent_check.missing_constraints,
115
+ "introduced_assumptions": intent_check.introduced_assumptions,
116
+ },
117
+ )
118
+ if runtime_context.strict_intent and not intent_check.preserved:
119
+ raise IntentViolationError("observable intent contract was not preserved")
120
+ ledger.require_within_budget()
121
+ except BudgetExceededError as exc:
122
+ monitor.record_failure(
123
+ exc,
124
+ category="budget_exhausted",
125
+ stage="intervention",
126
+ )
127
+ monitor.finish_run(ObservedRunStatus.BUDGET_EXHAUSTED, run_id=run_id)
128
+ raise
129
+ except IntentViolationError as exc:
130
+ monitor.record_failure(
131
+ exc,
132
+ category="intent_drift",
133
+ stage="intent_gate",
134
+ )
135
+ monitor.finish_run(ObservedRunStatus.INTENT_REJECTED, run_id=run_id)
136
+ raise
137
+
138
+ is_noop = all(operation.op is PatchOp.NO_OP for operation in patch.operations)
139
+ if patch.metadata.get("fallback"):
140
+ status = RunStatus.FALLBACK
141
+ elif patch.metadata.get("budget_exhausted"):
142
+ status = RunStatus.BUDGET_EXHAUSTED
143
+ elif is_noop:
144
+ status = RunStatus.NO_OP
145
+ else:
146
+ status = RunStatus.SUCCESS
147
+ monitor.emit(
148
+ EventKind.STAGE_FINISHED,
149
+ component="intervention",
150
+ payload={"status": status.value, "patch_hash": patch.patch_hash},
151
+ )
152
+ observed_status = ObservedRunStatus.SUCCEEDED
153
+ if status is RunStatus.BUDGET_EXHAUSTED:
154
+ error = BudgetExceededError("intervention reported budget exhaustion")
155
+ monitor.record_failure(
156
+ error,
157
+ category="budget_exhausted",
158
+ stage="intervention",
159
+ )
160
+ observed_status = ObservedRunStatus.BUDGET_EXHAUSTED
161
+ snapshot = monitor.finish_run(
162
+ observed_status,
163
+ run_id=run_id,
164
+ payload={"outcome": status.value},
165
+ )
166
+ return RunRecord(
167
+ release_id=intervention.release_id,
168
+ input_state_hash=state.state_hash,
169
+ output_state_hash=output.state_hash,
170
+ patch=patch,
171
+ status=status,
172
+ monitor_run_id=run_id,
173
+ duration_ms=snapshot.duration_ms,
174
+ usage=snapshot.usage,
175
+ intent_check=intent_check,
176
+ )
177
+
178
+ @staticmethod
179
+ def _reconcile_trace_usage(
180
+ patch: InputPatch | CompositeResult,
181
+ ledger: BudgetLedger,
182
+ monitor: RunMonitor,
183
+ ) -> None:
184
+ """Charge counters emitted by uninstrumented upstream workers once."""
185
+
186
+ counters = patch.trace.counters
187
+ current = ledger.usage
188
+ declared = ResourceUsage(
189
+ model_calls=_trace_integer_counter(counters, "model_calls"),
190
+ retrieval_calls=_trace_integer_counter(counters, "retrieval_calls"),
191
+ tool_calls=_trace_integer_counter(counters, "tool_calls"),
192
+ embedding_calls=_trace_integer_counter(counters, "embedding_calls"),
193
+ unpriced_model_calls=_trace_integer_counter(counters, "unpriced_model_calls"),
194
+ input_tokens=_trace_integer_counter(counters, "input_tokens"),
195
+ output_tokens=_trace_integer_counter(counters, "output_tokens"),
196
+ cpu_seconds=_trace_float_counter(counters, "cpu_seconds"),
197
+ accelerator_seconds=_trace_float_counter(counters, "accelerator_seconds"),
198
+ cost_usd=_trace_float_counter(counters, "cost_usd"),
199
+ compute_cost_usd=_trace_float_counter(counters, "compute_cost_usd"),
200
+ )
201
+ delta = ResourceUsage(
202
+ model_calls=max(0, declared.model_calls - current.model_calls),
203
+ retrieval_calls=max(0, declared.retrieval_calls - current.retrieval_calls),
204
+ tool_calls=max(0, declared.tool_calls - current.tool_calls),
205
+ embedding_calls=max(0, declared.embedding_calls - current.embedding_calls),
206
+ unpriced_model_calls=max(
207
+ 0,
208
+ declared.unpriced_model_calls - current.unpriced_model_calls,
209
+ ),
210
+ input_tokens=max(0, declared.input_tokens - current.input_tokens),
211
+ output_tokens=max(0, declared.output_tokens - current.output_tokens),
212
+ cpu_seconds=max(0.0, declared.cpu_seconds - current.cpu_seconds),
213
+ accelerator_seconds=max(
214
+ 0.0,
215
+ declared.accelerator_seconds - current.accelerator_seconds,
216
+ ),
217
+ cost_usd=max(0.0, declared.cost_usd - current.cost_usd),
218
+ compute_cost_usd=max(
219
+ 0.0,
220
+ declared.compute_cost_usd - current.compute_cost_usd,
221
+ ),
222
+ )
223
+ if delta != ResourceUsage():
224
+ try:
225
+ ledger.charge_actual(delta)
226
+ except BudgetExceededError:
227
+ monitor.record_usage(delta, component="upstream_trace")
228
+ monitor.emit(EventKind.BUDGET_EXCEEDED, component="upstream_trace")
229
+ raise
230
+ monitor.record_usage(delta, component="upstream_trace")
231
+
232
+
233
+ def _trace_integer_counter(counters: Mapping[str, object], name: str) -> int:
234
+ value = counters.get(name, 0)
235
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
236
+ raise ValidationError(
237
+ f"trace counter {name!r} must be a non-negative integer; got {value!r}"
238
+ )
239
+ return value
240
+
241
+
242
+ def _trace_float_counter(counters: Mapping[str, object], name: str) -> float:
243
+ value = counters.get(name, 0.0)
244
+ if (
245
+ isinstance(value, bool)
246
+ or not isinstance(value, (int, float))
247
+ or value < 0
248
+ or not math.isfinite(value)
249
+ ):
250
+ raise ValidationError(
251
+ f"trace counter {name!r} must be finite and non-negative; got {value!r}"
252
+ )
253
+ return float(value)
254
+
255
+
256
+ def _reject_hidden_runtime_dependencies(intervention: Intervention) -> None:
257
+ """Keep executable dependencies on the context, independent of field names.
258
+
259
+ Upstream adapters may call an LLM field ``generator`` or nest a backend in a
260
+ small configuration holder. Looking only for attributes literally named
261
+ ``backend`` would let those calls bypass monitoring, budgets, and balance
262
+ fingerprints. This bounded walk inspects the intervention's declared
263
+ state and data containers, but never descends into arbitrary dependency
264
+ implementations.
265
+ """
266
+
267
+ hidden = _hidden_runtime_dependency_paths(intervention)
268
+ if hidden:
269
+ names = ", ".join(hidden)
270
+ raise ValidationError(
271
+ "runtime dependencies must be injected through InterventionContext "
272
+ f"for monitoring and balance audit; hidden on intervention: {names}"
273
+ )
274
+
275
+
276
+ def _hidden_runtime_dependency_paths(intervention: Intervention) -> list[str]:
277
+ return sorted(
278
+ {
279
+ node.path
280
+ for node in runtime_dependency_nodes(
281
+ intervention,
282
+ root_path="intervention",
283
+ )
284
+ }
285
+ )
@@ -0,0 +1,268 @@
1
+ """Bounded object-graph inspection for executable runtime dependencies."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass, fields, is_dataclass
7
+ from functools import partial
8
+ from types import BuiltinFunctionType, FunctionType, MethodType, ModuleType
9
+ from typing import Any
10
+
11
+ from agentinputlab.backends.llm import LLMBackend
12
+ from agentinputlab.backends.retrieval import Retriever
13
+ from agentinputlab.backends.vector import DenseEmbedder, VectorSearcher
14
+ from agentinputlab.connectors.tools import ToolConnector
15
+ from agentinputlab.contracts.errors import ValidationError
16
+
17
+ RUNTIME_DEPENDENCY_TYPES = (
18
+ LLMBackend,
19
+ DenseEmbedder,
20
+ Retriever,
21
+ VectorSearcher,
22
+ ToolConnector,
23
+ )
24
+
25
+ _EXTERNAL_EXECUTOR_METHODS = ("call", "invoke", "reformulate", "run")
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class ObjectGraphNode:
30
+ path: str
31
+ value: object
32
+
33
+
34
+ def inspect_object_graph(
35
+ root: object,
36
+ *,
37
+ root_path: str,
38
+ terminal_types: tuple[type[Any], ...] = (),
39
+ traverse_runtime_delegates: bool = False,
40
+ max_depth: int = 8,
41
+ max_nodes: int = 4096,
42
+ ) -> tuple[ObjectGraphNode, ...]:
43
+ """Return a cycle-safe snapshot of declared object state and callable captures.
44
+
45
+ Mapping keys, bound-method owners, function defaults/closures/referenced
46
+ globals, partial arguments, dataclass fields, ``__dict__``, and ``__slots__``
47
+ are included. The walk never executes user callables. Hitting a bound is
48
+ an error rather than a silent partial audit.
49
+ """
50
+
51
+ nodes: list[ObjectGraphNode] = []
52
+ visited: set[int] = set()
53
+
54
+ def walk_function_captures(
55
+ function: FunctionType,
56
+ path: str,
57
+ *,
58
+ depth: int,
59
+ include_globals: bool,
60
+ ) -> None:
61
+ walk(function.__defaults__, f"{path}.__defaults__", depth=depth + 1)
62
+ walk(function.__kwdefaults__, f"{path}.__kwdefaults__", depth=depth + 1)
63
+ for index, cell in enumerate(function.__closure__ or ()):
64
+ try:
65
+ captured = cell.cell_contents
66
+ except ValueError: # empty closure cell
67
+ continue
68
+ walk(captured, f"{path}.__closure__[{index}]", depth=depth + 1)
69
+ if include_globals:
70
+ for name in function.__code__.co_names:
71
+ if name in function.__globals__:
72
+ walk(
73
+ function.__globals__[name],
74
+ f"{path}.__globals__[{name}]",
75
+ depth=depth + 1,
76
+ )
77
+
78
+ def walk(value: object, path: str, *, depth: int) -> None:
79
+ if value is None or isinstance(value, (str, bytes, int, float, bool, type, ModuleType)):
80
+ return
81
+ if depth > max_depth:
82
+ raise ValidationError(
83
+ f"runtime-dependency inspection exceeded depth {max_depth} at {path}"
84
+ )
85
+
86
+ identity = id(value)
87
+ if identity in visited:
88
+ return
89
+ visited.add(identity)
90
+ nodes.append(ObjectGraphNode(path, value))
91
+ if len(nodes) > max_nodes:
92
+ raise ValidationError(f"runtime-dependency inspection exceeded {max_nodes} objects")
93
+ if terminal_types and isinstance(value, terminal_types):
94
+ return
95
+ # An executable dependency is normally an auditable boundary: callers
96
+ # bind its public fingerprint and execution instance, while its private
97
+ # model graph (for example a SentenceTransformer/PyTorch module tree)
98
+ # is not part of hidden-dependency discovery. Balance checks may opt
99
+ # into following only direct runtime delegates and ledgers so a proxy
100
+ # cannot conceal a shared executable object without opening the full
101
+ # model graph.
102
+ if is_runtime_dependency(value):
103
+ if traverse_runtime_delegates:
104
+ for name, item in _instance_state_items(value):
105
+ if is_runtime_dependency(item) or (
106
+ terminal_types and isinstance(item, terminal_types)
107
+ ):
108
+ walk(item, f"{path}.{name}", depth=depth + 1)
109
+ return
110
+
111
+ if isinstance(value, Mapping):
112
+ for index, (key, item) in enumerate(value.items()):
113
+ walk(key, f"{path}.key[{index}]", depth=depth + 1)
114
+ walk(item, f"{path}.value[{index}]", depth=depth + 1)
115
+ return
116
+ if isinstance(value, (tuple, list, set, frozenset)):
117
+ for index, item in enumerate(value):
118
+ walk(item, f"{path}[{index}]", depth=depth + 1)
119
+ return
120
+ if isinstance(value, partial):
121
+ walk(value.func, f"{path}.func", depth=depth + 1)
122
+ walk(value.args, f"{path}.args", depth=depth + 1)
123
+ walk(value.keywords, f"{path}.keywords", depth=depth + 1)
124
+ return
125
+ if isinstance(value, MethodType):
126
+ walk(value.__self__, f"{path}.__self__", depth=depth + 1)
127
+ walk(value.__func__, f"{path}.__func__", depth=depth + 1)
128
+ return
129
+ if isinstance(value, FunctionType):
130
+ walk_function_captures(value, path, depth=depth, include_globals=True)
131
+ return
132
+ if isinstance(value, BuiltinFunctionType):
133
+ owner = getattr(value, "__self__", None)
134
+ if owner is not None:
135
+ walk(owner, f"{path}.__self__", depth=depth + 1)
136
+ return
137
+ if is_dataclass(value) and not isinstance(value, type):
138
+ for declared in fields(value):
139
+ walk(
140
+ getattr(value, declared.name),
141
+ f"{path}.{declared.name}",
142
+ depth=depth + 1,
143
+ )
144
+ else:
145
+ attributes = getattr(value, "__dict__", {})
146
+ if isinstance(attributes, Mapping):
147
+ for name, item in attributes.items():
148
+ walk(item, f"{path}.{name}", depth=depth + 1)
149
+ for owner in type(value).__mro__:
150
+ declared_slots = owner.__dict__.get("__slots__", ())
151
+ if isinstance(declared_slots, str):
152
+ declared_slots = (declared_slots,)
153
+ for name in declared_slots:
154
+ if name in {"__dict__", "__weakref__"} or not hasattr(value, name):
155
+ continue
156
+ walk(getattr(value, name), f"{path}.{name}", depth=depth + 1)
157
+ ignored_class_names = {
158
+ "__annotations__",
159
+ "__dict__",
160
+ "__doc__",
161
+ "__module__",
162
+ "__slots__",
163
+ "__weakref__",
164
+ }
165
+ for owner in type(value).__mro__:
166
+ for name, declared in owner.__dict__.items():
167
+ if name in ignored_class_names or (name.startswith("__") and name.endswith("__")):
168
+ continue
169
+ function: FunctionType | None = None
170
+ if isinstance(declared, (classmethod, staticmethod)):
171
+ unwrapped = declared.__func__
172
+ if isinstance(unwrapped, FunctionType):
173
+ function = unwrapped
174
+ elif isinstance(declared, FunctionType):
175
+ function = declared
176
+ if function is not None:
177
+ walk_function_captures(
178
+ function,
179
+ f"{path}.__class__.{name}",
180
+ depth=depth + 1,
181
+ include_globals=False,
182
+ )
183
+ continue
184
+ if isinstance(declared, property):
185
+ for accessor_name, accessor in (
186
+ ("fget", declared.fget),
187
+ ("fset", declared.fset),
188
+ ("fdel", declared.fdel),
189
+ ):
190
+ if isinstance(accessor, FunctionType):
191
+ walk_function_captures(
192
+ accessor,
193
+ f"{path}.__class__.{name}.{accessor_name}",
194
+ depth=depth + 1,
195
+ include_globals=False,
196
+ )
197
+ continue
198
+ if hasattr(declared, "__get__"):
199
+ continue
200
+ walk(declared, f"{path}.__class__.{name}", depth=depth + 1)
201
+
202
+ walk(root, root_path, depth=0)
203
+ return tuple(nodes)
204
+
205
+
206
+ def _instance_state_items(value: object) -> tuple[tuple[str, object], ...]:
207
+ """Return direct instance state once, without executing descriptors."""
208
+
209
+ items: dict[str, object] = {}
210
+ if is_dataclass(value) and not isinstance(value, type):
211
+ for declared in fields(value):
212
+ items[declared.name] = getattr(value, declared.name)
213
+ attributes = getattr(value, "__dict__", {})
214
+ if isinstance(attributes, Mapping):
215
+ items.update((str(name), item) for name, item in attributes.items())
216
+ for owner in type(value).__mro__:
217
+ declared_slots = owner.__dict__.get("__slots__", ())
218
+ if isinstance(declared_slots, str):
219
+ declared_slots = (declared_slots,)
220
+ for name in declared_slots:
221
+ if name in {"__dict__", "__weakref__"} or name in items:
222
+ continue
223
+ if hasattr(value, name):
224
+ items[name] = getattr(value, name)
225
+ return tuple(items.items())
226
+
227
+
228
+ def runtime_dependency_nodes(
229
+ root: object,
230
+ *,
231
+ root_path: str,
232
+ terminal_types: tuple[type[Any], ...] = (),
233
+ ) -> tuple[ObjectGraphNode, ...]:
234
+ return tuple(
235
+ node
236
+ for node in inspect_object_graph(
237
+ root,
238
+ root_path=root_path,
239
+ terminal_types=terminal_types,
240
+ )
241
+ if is_runtime_dependency(node.value)
242
+ )
243
+
244
+
245
+ def is_runtime_dependency(value: object) -> bool:
246
+ """Return whether ``value`` can perform an in-scope external execution.
247
+
248
+ The core backend protocols are nominally identifiable. Pinned upstream
249
+ bridges intentionally use small structural seams instead (``call``,
250
+ ``run``, or ``reformulate`` plus a stable fingerprint), so they must also
251
+ be treated as executable dependencies regardless of their field name.
252
+ """
253
+
254
+ if isinstance(value, RUNTIME_DEPENDENCY_TYPES):
255
+ return True
256
+ fingerprint = getattr(value, "fingerprint", None)
257
+ return callable(fingerprint) and any(
258
+ callable(getattr(value, method, None)) for method in _EXTERNAL_EXECUTOR_METHODS
259
+ )
260
+
261
+
262
+ __all__ = [
263
+ "RUNTIME_DEPENDENCY_TYPES",
264
+ "ObjectGraphNode",
265
+ "inspect_object_graph",
266
+ "is_runtime_dependency",
267
+ "runtime_dependency_nodes",
268
+ ]
@@ -0,0 +1,80 @@
1
+ """Strict external-worker clients.
2
+
3
+ The subprocess transport accepts an argv tuple and never invokes a shell.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ from collections.abc import Callable, Mapping
10
+ from dataclasses import dataclass, field
11
+ from typing import Protocol, runtime_checkable
12
+
13
+ from agentinputlab.contracts.errors import ExternalDependencyError
14
+ from agentinputlab.contracts.serialization import to_jsonable
15
+ from agentinputlab.contracts.worker import WorkerRequest, WorkerResponse
16
+ from agentinputlab.integrations.external_workers.process import run_bounded_process
17
+
18
+
19
+ @runtime_checkable
20
+ class WorkerClient(Protocol):
21
+ def invoke(self, request: WorkerRequest) -> WorkerResponse: ...
22
+
23
+
24
+ @dataclass(slots=True)
25
+ class FunctionWorkerClient(WorkerClient):
26
+ fn: Callable[[WorkerRequest], WorkerResponse]
27
+
28
+ def invoke(self, request: WorkerRequest) -> WorkerResponse:
29
+ return self.fn(request)
30
+
31
+
32
+ @dataclass(slots=True)
33
+ class JsonLineWorkerClient(WorkerClient):
34
+ argv: tuple[str, ...]
35
+ timeout_seconds: float = 300.0
36
+ max_request_bytes: int = 8 * 1024 * 1024
37
+ max_response_bytes: int = 8 * 1024 * 1024
38
+ max_stderr_bytes: int = 1024 * 1024
39
+ cwd: str | None = None
40
+ env: Mapping[str, str] = field(default_factory=dict)
41
+
42
+ def __post_init__(self) -> None:
43
+ self.argv = tuple(self.argv)
44
+ if not self.argv:
45
+ raise ValueError("worker argv must not be empty")
46
+ if min(self.max_request_bytes, self.max_response_bytes, self.max_stderr_bytes) < 1:
47
+ raise ValueError("worker byte limits must be positive")
48
+
49
+ def invoke(self, request: WorkerRequest) -> WorkerResponse:
50
+ payload = json.dumps(to_jsonable(request), sort_keys=True) + "\n"
51
+ completed = run_bounded_process(
52
+ self.argv,
53
+ input_text=payload,
54
+ timeout_seconds=self.timeout_seconds,
55
+ max_input_bytes=self.max_request_bytes,
56
+ max_stdout_bytes=self.max_response_bytes,
57
+ max_stderr_bytes=self.max_stderr_bytes,
58
+ cwd=self.cwd,
59
+ environment=self.env,
60
+ process_name="external worker",
61
+ )
62
+ if completed.returncode != 0:
63
+ stderr = completed.stderr[-2000:]
64
+ raise ExternalDependencyError(
65
+ f"external worker exited {completed.returncode}: {stderr}"
66
+ )
67
+ lines = [line for line in completed.stdout.splitlines() if line.strip()]
68
+ if len(lines) != 1:
69
+ raise ExternalDependencyError("external worker must emit exactly one JSON line")
70
+ try:
71
+ data = json.loads(lines[0])
72
+ except json.JSONDecodeError as exc:
73
+ raise ExternalDependencyError("external worker returned invalid JSON") from exc
74
+ try:
75
+ response = WorkerResponse(**data)
76
+ except (TypeError, ValueError) as exc:
77
+ raise ExternalDependencyError("external worker returned an invalid envelope") from exc
78
+ if response.request_id != request.request_id:
79
+ raise ExternalDependencyError("external worker response request_id mismatch")
80
+ return response