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,777 @@
1
+ """Auditable in-memory dense retrieval with lazy SentenceTransformers loading.
2
+
3
+ The implementation deliberately separates a *profile identity* from weight
4
+ availability. The package ships model repository names, immutable revisions,
5
+ and prompt contracts, but it never bundles or silently downloads checkpoints.
6
+ By default, the SentenceTransformers loader is restricted to an existing local
7
+ cache. Tests and institutional deployments may instead inject a compatible
8
+ encoder with an explicit fingerprint.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import importlib
14
+ import math
15
+ import re
16
+ from collections.abc import Iterable, Mapping, Sequence
17
+ from dataclasses import dataclass, field
18
+ from importlib import metadata
19
+ from pathlib import Path
20
+ from types import MappingProxyType
21
+ from typing import Any, Protocol, runtime_checkable
22
+
23
+ from agentinputlab.backends.embedding.base import (
24
+ EmbeddingBackend,
25
+ EmbeddingRequest,
26
+ EmbeddingResponse,
27
+ validate_embedding_response,
28
+ )
29
+ from agentinputlab.contracts.errors import ExternalDependencyError, ValidationError
30
+ from agentinputlab.contracts.serialization import content_hash, freeze_mapping
31
+
32
+ from .base import Document, RetrievalResult, Retriever
33
+
34
+ _HEX40 = re.compile(r"^[0-9a-f]{40}$")
35
+ _DENSE_BACKEND = "sentence_transformers_dense"
36
+ _BM25_BACKEND = "memory_bm25"
37
+
38
+
39
+ @dataclass(frozen=True, slots=True)
40
+ class RetrieverProfile:
41
+ """Immutable retriever configuration used in comparison fingerprints.
42
+
43
+ ``weights_bundled`` and ``weight_files_verified`` are intentionally fixed
44
+ to ``False`` for the model profiles below. A repository revision is useful
45
+ provenance, but it is not a substitute for hashing a user's local weight
46
+ files.
47
+ """
48
+
49
+ profile_id: str
50
+ backend: str
51
+ model_id: str = ""
52
+ model_revision: str = ""
53
+ query_prefix: str = ""
54
+ document_prefix: str = ""
55
+ expected_dimensions: int | None = None
56
+ metric: str = "cosine"
57
+ tokenizer_padding_side: str | None = None
58
+ license_spdx: str = "NOASSERTION"
59
+ source_url: str = ""
60
+ weights_bundled: bool = False
61
+ weight_files_verified: bool = False
62
+
63
+ def __post_init__(self) -> None:
64
+ if not self.profile_id or not self.backend:
65
+ raise ValidationError("retriever profile requires profile_id and backend")
66
+ if self.backend not in {_BM25_BACKEND, _DENSE_BACKEND}:
67
+ raise ValidationError(f"unsupported retriever profile backend: {self.backend}")
68
+ if self.weights_bundled or self.weight_files_verified:
69
+ raise ValidationError("built-in retriever profiles must not claim bundled weights")
70
+ if self.backend == _BM25_BACKEND:
71
+ if self.model_id or self.model_revision or self.expected_dimensions is not None:
72
+ raise ValidationError("BM25 profile must not declare dense model fields")
73
+ if self.metric != "bm25":
74
+ raise ValidationError("BM25 profile metric must be bm25")
75
+ return
76
+ if not self.model_id or not _HEX40.fullmatch(self.model_revision):
77
+ raise ValidationError("dense profile requires a model repository and 40-hex revision")
78
+ if (
79
+ self.expected_dimensions is None
80
+ or isinstance(self.expected_dimensions, bool)
81
+ or not isinstance(self.expected_dimensions, int)
82
+ or self.expected_dimensions < 1
83
+ ):
84
+ raise ValidationError("dense profile requires positive expected_dimensions")
85
+ if self.metric != "cosine":
86
+ raise ValidationError("SentenceTransformer dense profiles use cosine")
87
+ if self.tokenizer_padding_side not in {None, "left", "right"}:
88
+ raise ValidationError("tokenizer_padding_side must be left, right, or None")
89
+
90
+
91
+ _PROFILE_ITEMS = (
92
+ RetrieverProfile(
93
+ profile_id="sparse.bm25.memory.v1",
94
+ backend=_BM25_BACKEND,
95
+ metric="bm25",
96
+ license_spdx="MIT",
97
+ source_url="https://github.com/Data-Designer/AgentInputLab",
98
+ ),
99
+ RetrieverProfile(
100
+ profile_id="dense.bge.base_en_v1_5",
101
+ backend=_DENSE_BACKEND,
102
+ model_id="BAAI/bge-base-en-v1.5",
103
+ model_revision="a5beb1e3e68b9ab74eb54cfd186867f64f240e1a",
104
+ query_prefix="Represent this sentence for searching relevant passages: ",
105
+ expected_dimensions=768,
106
+ license_spdx="MIT",
107
+ source_url="https://huggingface.co/BAAI/bge-base-en-v1.5",
108
+ ),
109
+ RetrieverProfile(
110
+ profile_id="dense.bge.m3",
111
+ backend=_DENSE_BACKEND,
112
+ model_id="BAAI/bge-m3",
113
+ model_revision="5617a9f61b028005a4858fdac845db406aefb181",
114
+ expected_dimensions=1024,
115
+ license_spdx="MIT",
116
+ source_url="https://huggingface.co/BAAI/bge-m3",
117
+ ),
118
+ RetrieverProfile(
119
+ profile_id="dense.e5.base_v2",
120
+ backend=_DENSE_BACKEND,
121
+ model_id="intfloat/e5-base-v2",
122
+ model_revision="f52bf8ec8c7124536f0efb74aca902b2995e5bcd",
123
+ query_prefix="query: ",
124
+ document_prefix="passage: ",
125
+ expected_dimensions=768,
126
+ license_spdx="MIT",
127
+ source_url="https://huggingface.co/intfloat/e5-base-v2",
128
+ ),
129
+ RetrieverProfile(
130
+ profile_id="dense.e5.large_v2",
131
+ backend=_DENSE_BACKEND,
132
+ model_id="intfloat/e5-large-v2",
133
+ model_revision="f169b11e22de13617baa190a028a32f3493550b6",
134
+ query_prefix="query: ",
135
+ document_prefix="passage: ",
136
+ expected_dimensions=1024,
137
+ license_spdx="MIT",
138
+ source_url="https://huggingface.co/intfloat/e5-large-v2",
139
+ ),
140
+ RetrieverProfile(
141
+ profile_id="dense.qwen3.embedding_0_6b",
142
+ backend=_DENSE_BACKEND,
143
+ model_id="Qwen/Qwen3-Embedding-0.6B",
144
+ model_revision="97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3",
145
+ query_prefix=(
146
+ "Instruct: Given a web search query, retrieve relevant passages that answer "
147
+ "the query\nQuery:"
148
+ ),
149
+ expected_dimensions=1024,
150
+ tokenizer_padding_side="left",
151
+ license_spdx="Apache-2.0",
152
+ source_url="https://huggingface.co/Qwen/Qwen3-Embedding-0.6B",
153
+ ),
154
+ RetrieverProfile(
155
+ profile_id="dense.contriever.msmarco",
156
+ backend=_DENSE_BACKEND,
157
+ model_id="facebook/contriever-msmarco",
158
+ model_revision="abe8c1493371369031bcb1e02acb754cf4e162fa",
159
+ expected_dimensions=768,
160
+ license_spdx="CC-BY-NC-4.0",
161
+ source_url="https://huggingface.co/facebook/contriever-msmarco",
162
+ ),
163
+ )
164
+
165
+ RETRIEVER_PROFILES: Mapping[str, RetrieverProfile] = MappingProxyType(
166
+ {profile.profile_id: profile for profile in _PROFILE_ITEMS}
167
+ )
168
+ DENSE_RETRIEVER_PROFILE_IDS = tuple(
169
+ profile.profile_id for profile in _PROFILE_ITEMS if profile.backend == _DENSE_BACKEND
170
+ )
171
+
172
+
173
+ def retriever_profile(profile_id: str) -> RetrieverProfile:
174
+ """Return one built-in profile, failing closed on an unknown identity."""
175
+
176
+ try:
177
+ return RETRIEVER_PROFILES[profile_id]
178
+ except KeyError as exc:
179
+ raise ValidationError(f"unknown retriever profile: {profile_id}") from exc
180
+
181
+
182
+ @runtime_checkable
183
+ class BatchTextEncoder(Protocol):
184
+ """Minimal SentenceTransformers-compatible injection seam."""
185
+
186
+ def encode(
187
+ self,
188
+ sentences: Sequence[str],
189
+ *,
190
+ batch_size: int,
191
+ normalize_embeddings: bool,
192
+ show_progress_bar: bool,
193
+ convert_to_numpy: bool,
194
+ ) -> Any:
195
+ """Encode one bounded batch in input order."""
196
+
197
+
198
+ @dataclass(frozen=True, slots=True)
199
+ class EmbeddingBackendEncoder:
200
+ """Adapt an AgentInputLab embedding backend to the dense-retriever seam.
201
+
202
+ The backend may be a local callable, SDK/RPC bridge, replay fixture, or
203
+ self-hosted/API client. Its secret-free fingerprint is captured once and
204
+ becomes part of the retriever identity. Prompt prefixes remain the selected
205
+ :class:`RetrieverProfile`'s responsibility and are applied before this
206
+ adapter is called.
207
+ """
208
+
209
+ backend: EmbeddingBackend
210
+ dimensions: int | None = None
211
+ request_metadata: Mapping[str, Any] = field(default_factory=dict)
212
+ _backend_identity: Mapping[str, Any] = field(init=False, repr=False)
213
+
214
+ def __post_init__(self) -> None:
215
+ if not isinstance(self.backend, EmbeddingBackend):
216
+ raise ValidationError("embedding encoder requires an EmbeddingBackend")
217
+ if self.dimensions is not None and (
218
+ isinstance(self.dimensions, bool)
219
+ or not isinstance(self.dimensions, int)
220
+ or self.dimensions < 1
221
+ ):
222
+ raise ValidationError("embedding encoder dimensions must be positive or None")
223
+ identity = self.backend.fingerprint()
224
+ if not isinstance(identity, Mapping) or not identity:
225
+ raise ValidationError("embedding backend fingerprint must be a non-empty mapping")
226
+ try:
227
+ frozen_identity = freeze_mapping(identity)
228
+ frozen_metadata = freeze_mapping(self.request_metadata)
229
+ except (TypeError, ValueError) as exc:
230
+ raise ValidationError(
231
+ "embedding encoder identity must be canonically serializable"
232
+ ) from exc
233
+ object.__setattr__(self, "_backend_identity", frozen_identity)
234
+ object.__setattr__(self, "request_metadata", frozen_metadata)
235
+
236
+ def encode(
237
+ self,
238
+ sentences: Sequence[str],
239
+ *,
240
+ batch_size: int,
241
+ normalize_embeddings: bool,
242
+ show_progress_bar: bool,
243
+ convert_to_numpy: bool,
244
+ ) -> tuple[tuple[float, ...], ...]:
245
+ del batch_size, show_progress_bar, convert_to_numpy
246
+ self._require_stable_backend_identity()
247
+ request = EmbeddingRequest(
248
+ texts=tuple(sentences),
249
+ normalize=normalize_embeddings,
250
+ dimensions=self.dimensions,
251
+ metadata=self.request_metadata,
252
+ )
253
+ response = self.backend.embed(request)
254
+ if not isinstance(response, EmbeddingResponse):
255
+ raise ValidationError("embedding backend returned a non-EmbeddingResponse")
256
+ return validate_embedding_response(
257
+ request,
258
+ response,
259
+ backend_dimensions=self.dimensions,
260
+ ).vectors
261
+
262
+ def fingerprint(self) -> Mapping[str, Any]:
263
+ self._require_stable_backend_identity()
264
+ return {
265
+ "implementation": "agentinputlab.embedding_backend_encoder.v1",
266
+ "backend_identity": self._backend_identity,
267
+ "dimensions": self.dimensions,
268
+ "request_metadata_hash": content_hash(self.request_metadata),
269
+ }
270
+
271
+ def _require_stable_backend_identity(self) -> None:
272
+ identity = self.backend.fingerprint()
273
+ if not isinstance(identity, Mapping) or not identity:
274
+ raise ValidationError("embedding backend fingerprint must be a non-empty mapping")
275
+ try:
276
+ current = freeze_mapping(identity)
277
+ except (TypeError, ValueError) as exc:
278
+ raise ValidationError(
279
+ "embedding encoder identity must be canonically serializable"
280
+ ) from exc
281
+ if current != self._backend_identity:
282
+ raise ValidationError("embedding backend fingerprint changed after adapter creation")
283
+
284
+
285
+ @dataclass(frozen=True, slots=True)
286
+ class DenseIndexSummary:
287
+ document_count: int
288
+ dimensions: int
289
+ vector_hash: str
290
+
291
+
292
+ class SentenceTransformerDenseRetriever(Retriever):
293
+ """Batch-index documents and rank them by deterministic cosine similarity.
294
+
295
+ Construction and :meth:`fingerprint` never import SentenceTransformers or
296
+ touch the network. The first :meth:`build_index` or :meth:`retrieve` call
297
+ loads the pinned model. ``local_files_only=True`` is the safe default; set
298
+ it to ``False`` explicitly if downloading the pinned revision is intended.
299
+ """
300
+
301
+ __slots__ = (
302
+ "_batch_size",
303
+ "_dependency_version",
304
+ "_device",
305
+ "_documents",
306
+ "_encoder",
307
+ "_encoder_identity",
308
+ "_encoder_identity_binding",
309
+ "_encoder_identity_probe",
310
+ "_index_summary",
311
+ "_local_files_only",
312
+ "_model_path",
313
+ "_profile",
314
+ "_retriever_id",
315
+ "_runtime_kind",
316
+ "_vectors",
317
+ )
318
+
319
+ def __init__(
320
+ self,
321
+ documents: Iterable[Document],
322
+ *,
323
+ profile: RetrieverProfile | str,
324
+ batch_size: int = 32,
325
+ encoder: BatchTextEncoder | None = None,
326
+ encoder_identity: Mapping[str, Any] | None = None,
327
+ device: str | None = None,
328
+ local_files_only: bool = True,
329
+ model_path: Path | str | None = None,
330
+ retriever_id: str | None = None,
331
+ ) -> None:
332
+ resolved = retriever_profile(profile) if isinstance(profile, str) else profile
333
+ if not isinstance(resolved, RetrieverProfile) or resolved.backend != _DENSE_BACKEND:
334
+ raise ValidationError("SentenceTransformerDenseRetriever requires a dense profile")
335
+ if isinstance(batch_size, bool) or not isinstance(batch_size, int) or batch_size < 1:
336
+ raise ValidationError("dense retriever batch_size must be a positive integer")
337
+ if device is not None and (not isinstance(device, str) or not device.strip()):
338
+ raise ValidationError("dense retriever device must be non-empty or None")
339
+ if not isinstance(local_files_only, bool):
340
+ raise ValidationError("local_files_only must be boolean")
341
+ resolved_model_path: Path | None = None
342
+ if model_path is not None:
343
+ try:
344
+ resolved_model_path = Path(model_path)
345
+ except TypeError as exc:
346
+ raise ValidationError("dense model_path must be path-like or None") from exc
347
+ if not resolved_model_path.is_absolute():
348
+ raise ValidationError("dense model_path must be absolute")
349
+ prepared_documents = tuple(documents)
350
+ if not all(isinstance(document, Document) for document in prepared_documents):
351
+ raise ValidationError("dense retriever documents must be Document instances")
352
+ document_ids = tuple(document.doc_id for document in prepared_documents)
353
+ if len(set(document_ids)) != len(document_ids):
354
+ raise ValidationError("dense retriever document IDs must be unique")
355
+ if encoder is None and encoder_identity is not None:
356
+ raise ValidationError("encoder_identity is only valid with an injected encoder")
357
+ if encoder is not None and not isinstance(encoder, BatchTextEncoder):
358
+ raise ValidationError("injected dense encoder must implement encode()")
359
+ if encoder is not None:
360
+ identity = encoder_identity
361
+ identity_probe = None
362
+ if identity is None:
363
+ fingerprint = getattr(encoder, "fingerprint", None)
364
+ identity = fingerprint() if callable(fingerprint) else None
365
+ identity_probe = fingerprint if callable(fingerprint) else None
366
+ if not isinstance(identity, Mapping) or not identity:
367
+ raise ValidationError("injected dense encoder requires a non-empty identity")
368
+ try:
369
+ frozen_encoder_identity = freeze_mapping(identity)
370
+ except (TypeError, ValueError) as exc:
371
+ raise ValidationError(
372
+ "injected dense encoder identity must be canonically serializable"
373
+ ) from exc
374
+ identity_binding = (
375
+ "derived_fingerprint_monitored"
376
+ if identity_probe is not None
377
+ else "explicit_identity_unverified"
378
+ )
379
+ runtime_kind = "injected_encoder_unverified"
380
+ dependency_version = "injected"
381
+ else:
382
+ frozen_encoder_identity = freeze_mapping(
383
+ {
384
+ "model_id": resolved.model_id,
385
+ "model_revision": resolved.model_revision,
386
+ "loader": "sentence_transformers.SentenceTransformer",
387
+ }
388
+ )
389
+ identity_probe = None
390
+ identity_binding = "pinned_profile_revision"
391
+ runtime_kind = "sentence_transformers_lazy"
392
+ dependency_version = _distribution_version("sentence-transformers")
393
+
394
+ resolved_retriever_id = retriever_id or f"dense:{resolved.profile_id}"
395
+ if not resolved_retriever_id:
396
+ raise ValidationError("dense retriever_id must be non-empty")
397
+ self._documents = prepared_documents
398
+ self._profile = resolved
399
+ self._batch_size = batch_size
400
+ self._device = device
401
+ self._local_files_only = local_files_only
402
+ self._model_path = resolved_model_path
403
+ self._retriever_id = resolved_retriever_id
404
+ self._encoder = encoder
405
+ self._encoder_identity = frozen_encoder_identity
406
+ self._encoder_identity_binding = identity_binding
407
+ self._encoder_identity_probe = identity_probe
408
+ self._runtime_kind = runtime_kind
409
+ self._dependency_version = dependency_version
410
+ self._vectors: tuple[tuple[float, ...], ...] | None = None
411
+ self._index_summary: DenseIndexSummary | None = None
412
+
413
+ @property
414
+ def documents(self) -> tuple[Document, ...]:
415
+ return self._documents
416
+
417
+ @property
418
+ def profile(self) -> RetrieverProfile:
419
+ return self._profile
420
+
421
+ @property
422
+ def batch_size(self) -> int:
423
+ return self._batch_size
424
+
425
+ @property
426
+ def device(self) -> str | None:
427
+ return self._device
428
+
429
+ @property
430
+ def local_files_only(self) -> bool:
431
+ return self._local_files_only
432
+
433
+ @property
434
+ def retriever_id(self) -> str:
435
+ return self._retriever_id
436
+
437
+ @property
438
+ def embedder_id(self) -> str:
439
+ """Identity exposed through the paper-level ``DenseEmbedder`` seam."""
440
+
441
+ return f"{self._retriever_id}:embedder"
442
+
443
+ @property
444
+ def searcher_id(self) -> str:
445
+ """Identity exposed through the paper-level ``VectorSearcher`` seam."""
446
+
447
+ return f"{self._retriever_id}:index"
448
+
449
+ @property
450
+ def indexed(self) -> bool:
451
+ return self._vectors is not None
452
+
453
+ def build_index(self) -> DenseIndexSummary:
454
+ """Encode the fixed corpus in bounded batches exactly once."""
455
+
456
+ if self._index_summary is not None:
457
+ return self._index_summary
458
+ if not self.documents:
459
+ summary = DenseIndexSummary(
460
+ document_count=0,
461
+ dimensions=self.profile.expected_dimensions or 0,
462
+ vector_hash=content_hash(()),
463
+ )
464
+ self._vectors = ()
465
+ self._index_summary = summary
466
+ return summary
467
+ inputs = tuple(self._document_input(document) for document in self.documents)
468
+ vectors = self._encode_batched(inputs, role="documents")
469
+ summary = DenseIndexSummary(
470
+ document_count=len(self.documents),
471
+ dimensions=len(vectors[0]),
472
+ vector_hash=content_hash(vectors),
473
+ )
474
+ self._vectors = vectors
475
+ self._index_summary = summary
476
+ return summary
477
+
478
+ def retrieve(self, query: str, *, top_k: int = 5) -> tuple[RetrievalResult, ...]:
479
+ if not isinstance(query, str):
480
+ raise ValidationError("dense retrieval query must be text")
481
+ if isinstance(top_k, bool) or not isinstance(top_k, int):
482
+ raise ValidationError("dense retrieval top_k must be an integer")
483
+ if top_k < 1 or not self.documents:
484
+ return ()
485
+ self.build_index()
486
+ assert self._vectors is not None # established by build_index
487
+ query_vector = self._encode_batched(
488
+ (f"{self.profile.query_prefix}{query}",),
489
+ role="query",
490
+ )[0]
491
+ return self.search(query_vector, top_k=top_k)
492
+
493
+ def embed(self, texts: Sequence[str]) -> tuple[tuple[float, ...], ...]:
494
+ """Encode query surrogates for HyDE using the profile's query contract."""
495
+
496
+ if isinstance(texts, (str, bytes)) or not isinstance(texts, Sequence):
497
+ raise ValidationError("dense embed texts must be a sequence of strings")
498
+ if any(not isinstance(text, str) for text in texts):
499
+ raise ValidationError("dense embed texts must contain only strings")
500
+ return self._encode_batched(
501
+ tuple(f"{self.profile.query_prefix}{text}" for text in texts),
502
+ role="query",
503
+ )
504
+
505
+ def search(
506
+ self,
507
+ vector: Sequence[float],
508
+ *,
509
+ top_k: int,
510
+ ) -> tuple[RetrievalResult, ...]:
511
+ """Search the fixed corpus with an already-computed dense query vector."""
512
+
513
+ if isinstance(top_k, bool) or not isinstance(top_k, int):
514
+ raise ValidationError("dense vector-search top_k must be an integer")
515
+ if top_k < 1 or not self.documents:
516
+ return ()
517
+ self.build_index()
518
+ assert self._vectors is not None
519
+ try:
520
+ prepared = tuple(float(value) for value in vector)
521
+ except (TypeError, ValueError, OverflowError) as exc:
522
+ raise ValidationError("dense vector-search query must be numeric") from exc
523
+ expected = self.profile.expected_dimensions
524
+ if len(prepared) != expected:
525
+ raise ValidationError(
526
+ f"dense vector-search query dimension does not match profile {expected}"
527
+ )
528
+ query_vector = _normalized_vector(prepared, role="query")
529
+ scored = [
530
+ (sum(left * right for left, right in zip(query_vector, item, strict=True)), document)
531
+ for document, item in zip(self.documents, self._vectors, strict=True)
532
+ ]
533
+ scored.sort(key=lambda item: (-item[0], item[1].doc_id))
534
+ return tuple(
535
+ RetrievalResult(
536
+ document=document,
537
+ score=score,
538
+ rank=rank,
539
+ features={"cosine_score": score},
540
+ )
541
+ for rank, (score, document) in enumerate(scored[:top_k], start=1)
542
+ )
543
+
544
+ def fingerprint(self) -> Mapping[str, Any]:
545
+ """Return a stable recipe identity without loading model weights."""
546
+
547
+ self._require_stable_encoder_identity()
548
+ fingerprint = {
549
+ "retriever_id": self.retriever_id,
550
+ "implementation": "agentinputlab.sentence_transformer_dense.v3",
551
+ "profile_id": self.profile.profile_id,
552
+ "model_id": self.profile.model_id,
553
+ "model_revision": self.profile.model_revision,
554
+ "expected_dimensions": self.profile.expected_dimensions,
555
+ "metric": "cosine",
556
+ "normalization": "agentinputlab.l2.v1",
557
+ "query_prefix": self.profile.query_prefix,
558
+ "document_prefix": self.profile.document_prefix,
559
+ "document_format": "title_newline_text.v1",
560
+ "tokenizer_padding_side": self.profile.tokenizer_padding_side,
561
+ "corpus_hash": content_hash(self.documents),
562
+ "document_count": len(self.documents),
563
+ "batch_size": self.batch_size,
564
+ "device": self.device or "auto",
565
+ "local_files_only": self.local_files_only,
566
+ "model_source": "local_snapshot" if self._model_path is not None else "profile_cache",
567
+ "runtime_kind": self._runtime_kind,
568
+ "encoder_identity_hash": content_hash(self._encoder_identity),
569
+ "encoder_identity_binding": self._encoder_identity_binding,
570
+ "sentence_transformers_version": self._dependency_version,
571
+ "weights_bundled": False,
572
+ "weight_files_verified": False,
573
+ }
574
+ self._require_stable_encoder_identity()
575
+ return fingerprint
576
+
577
+ def _document_input(self, document: Document) -> str:
578
+ body = f"{document.title}\n{document.text}" if document.title else document.text
579
+ return f"{self.profile.document_prefix}{body}"
580
+
581
+ def _encoder_instance(self) -> BatchTextEncoder:
582
+ if self._encoder is not None:
583
+ return self._encoder
584
+ try:
585
+ sentence_transformers = importlib.import_module("sentence_transformers")
586
+ except ModuleNotFoundError as exc:
587
+ raise ExternalDependencyError(
588
+ "SentenceTransformers is unavailable; install agentinputlab[dense-retrieval]"
589
+ ) from exc
590
+ constructor = getattr(sentence_transformers, "SentenceTransformer", None)
591
+ if not callable(constructor):
592
+ raise ExternalDependencyError(
593
+ "sentence_transformers does not expose SentenceTransformer"
594
+ )
595
+ kwargs: dict[str, Any] = {
596
+ "revision": self.profile.model_revision,
597
+ "local_files_only": self.local_files_only,
598
+ "trust_remote_code": False,
599
+ }
600
+ if self.device is not None:
601
+ kwargs["device"] = self.device
602
+ if self.profile.tokenizer_padding_side is not None:
603
+ kwargs["tokenizer_kwargs"] = {
604
+ "padding_side": self.profile.tokenizer_padding_side,
605
+ }
606
+ try:
607
+ model_source = (
608
+ str(self._model_path) if self._model_path is not None else self.profile.model_id
609
+ )
610
+ encoder = constructor(model_source, **kwargs)
611
+ except Exception as exc:
612
+ raise ExternalDependencyError(
613
+ "the pinned dense model is unavailable; prefetch the exact revision or "
614
+ "explicitly allow a download"
615
+ ) from exc
616
+ if not isinstance(encoder, BatchTextEncoder):
617
+ raise ExternalDependencyError("loaded SentenceTransformer has no compatible encode()")
618
+ self._encoder = encoder
619
+ return encoder
620
+
621
+ def _encode_batched(
622
+ self,
623
+ inputs: Sequence[str],
624
+ *,
625
+ role: str,
626
+ ) -> tuple[tuple[float, ...], ...]:
627
+ self._require_stable_encoder_identity()
628
+ encoder = self._encoder_instance()
629
+ rows: list[tuple[float, ...]] = []
630
+ for start in range(0, len(inputs), self.batch_size):
631
+ batch = tuple(inputs[start : start + self.batch_size])
632
+ self._require_stable_encoder_identity()
633
+ try:
634
+ raw = encoder.encode(
635
+ batch,
636
+ batch_size=len(batch),
637
+ normalize_embeddings=False,
638
+ show_progress_bar=False,
639
+ convert_to_numpy=True,
640
+ )
641
+ except ValidationError:
642
+ raise
643
+ except Exception as exc:
644
+ raise ExternalDependencyError(
645
+ f"dense encoder failed while encoding {role}"
646
+ ) from exc
647
+ self._require_stable_encoder_identity()
648
+ try:
649
+ converted = tuple(tuple(float(value) for value in vector) for vector in raw)
650
+ except (TypeError, ValueError, OverflowError) as exc:
651
+ raise ValidationError(f"dense encoder returned invalid {role} vectors") from exc
652
+ if len(converted) != len(batch):
653
+ raise ValidationError(f"dense encoder changed the {role} batch cardinality")
654
+ rows.extend(converted)
655
+ expected = self.profile.expected_dimensions
656
+ if any(len(vector) != expected for vector in rows):
657
+ raise ValidationError(
658
+ f"dense encoder output dimension does not match profile {expected}"
659
+ )
660
+ normalized = tuple(_normalized_vector(vector, role=role) for vector in rows)
661
+ return normalized
662
+
663
+ def _require_stable_encoder_identity(self) -> None:
664
+ probe = self._encoder_identity_probe
665
+ if probe is None:
666
+ return
667
+ try:
668
+ identity = probe()
669
+ except Exception as exc:
670
+ raise ValidationError("injected dense encoder fingerprint failed") from exc
671
+ if not isinstance(identity, Mapping) or not identity:
672
+ raise ValidationError("injected dense encoder fingerprint must be a non-empty mapping")
673
+ try:
674
+ current = freeze_mapping(identity)
675
+ except (TypeError, ValueError) as exc:
676
+ raise ValidationError(
677
+ "injected dense encoder identity must be canonically serializable"
678
+ ) from exc
679
+ if current != self._encoder_identity:
680
+ raise ValidationError("injected dense encoder fingerprint changed after construction")
681
+
682
+
683
+ def build_retriever(
684
+ profile_id: str,
685
+ documents: Iterable[Document],
686
+ **options: Any,
687
+ ) -> Retriever:
688
+ """Construct a built-in retriever profile without caller-side dispatch.
689
+
690
+ BM25 and dense profiles deliberately accept disjoint option sets. Unknown
691
+ or cross-family options fail closed instead of being silently ignored.
692
+ Dense callers may inject either a ``BatchTextEncoder`` or an
693
+ ``EmbeddingBackend``; the latter is wrapped with
694
+ :class:`EmbeddingBackendEncoder` and fingerprinted as part of the runtime.
695
+ """
696
+
697
+ profile = retriever_profile(profile_id)
698
+ if profile.backend == _BM25_BACKEND:
699
+ allowed = {"retriever_id", "k1", "b", "tokenizer", "tokenizer_id"}
700
+ _require_known_options(profile_id, options, allowed)
701
+ from .memory import InMemoryBM25Retriever
702
+
703
+ bm25_options = dict(options)
704
+ bm25_options.setdefault("retriever_id", f"bm25:{profile_id}")
705
+ return InMemoryBM25Retriever(documents=documents, **bm25_options)
706
+
707
+ allowed = {
708
+ "batch_size",
709
+ "device",
710
+ "embedding_backend",
711
+ "embedding_request_metadata",
712
+ "encoder",
713
+ "encoder_identity",
714
+ "local_files_only",
715
+ "model_path",
716
+ "retriever_id",
717
+ }
718
+ _require_known_options(profile_id, options, allowed)
719
+ dense_options = dict(options)
720
+ embedding_backend = dense_options.pop("embedding_backend", None)
721
+ request_metadata = dense_options.pop("embedding_request_metadata", None)
722
+ if embedding_backend is not None:
723
+ if dense_options.get("encoder") is not None:
724
+ raise ValidationError("dense retriever accepts encoder or embedding_backend, not both")
725
+ if dense_options.get("encoder_identity") is not None:
726
+ raise ValidationError(
727
+ "encoder_identity is derived from embedding_backend and must not be supplied"
728
+ )
729
+ dense_options["encoder"] = EmbeddingBackendEncoder(
730
+ backend=embedding_backend,
731
+ dimensions=profile.expected_dimensions,
732
+ request_metadata=request_metadata or {},
733
+ )
734
+ elif request_metadata is not None:
735
+ raise ValidationError("embedding_request_metadata requires embedding_backend")
736
+ return SentenceTransformerDenseRetriever(documents, profile=profile, **dense_options)
737
+
738
+
739
+ def _require_known_options(
740
+ profile_id: str,
741
+ options: Mapping[str, Any],
742
+ allowed: set[str],
743
+ ) -> None:
744
+ unknown = sorted(set(options) - allowed)
745
+ if unknown:
746
+ raise ValidationError(
747
+ f"retriever profile {profile_id} does not accept option(s): {', '.join(unknown)}"
748
+ )
749
+
750
+
751
+ def _normalized_vector(vector: tuple[float, ...], *, role: str) -> tuple[float, ...]:
752
+ if not vector or any(not math.isfinite(value) for value in vector):
753
+ raise ValidationError(f"dense encoder returned a non-finite or empty {role} vector")
754
+ norm = math.sqrt(sum(value * value for value in vector))
755
+ if not math.isfinite(norm) or norm == 0.0:
756
+ raise ValidationError(f"dense encoder returned a zero-norm {role} vector")
757
+ return tuple(value / norm for value in vector)
758
+
759
+
760
+ def _distribution_version(distribution: str) -> str:
761
+ try:
762
+ return metadata.version(distribution)
763
+ except metadata.PackageNotFoundError:
764
+ return "not-installed"
765
+
766
+
767
+ __all__ = [
768
+ "DENSE_RETRIEVER_PROFILE_IDS",
769
+ "RETRIEVER_PROFILES",
770
+ "BatchTextEncoder",
771
+ "DenseIndexSummary",
772
+ "EmbeddingBackendEncoder",
773
+ "RetrieverProfile",
774
+ "SentenceTransformerDenseRetriever",
775
+ "build_retriever",
776
+ "retriever_profile",
777
+ ]