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.
- agentinputlab/__init__.py +28 -0
- agentinputlab/algorithms/__init__.py +6 -0
- agentinputlab/algorithms/acquire/__init__.py +145 -0
- agentinputlab/algorithms/acquire/adaptive_rag/__init__.py +43 -0
- agentinputlab/algorithms/acquire/adaptive_rag/replay.py +367 -0
- agentinputlab/algorithms/acquire/adaptive_rag/runtime.py +1202 -0
- agentinputlab/algorithms/acquire/adaptive_rag_common_support.py +542 -0
- agentinputlab/algorithms/acquire/hyde.py +296 -0
- agentinputlab/algorithms/acquire/hyde_controlled.py +200 -0
- agentinputlab/algorithms/acquire/hyde_external.py +148 -0
- agentinputlab/algorithms/acquire/ircot.py +28 -0
- agentinputlab/algorithms/acquire/ircot_common_support.py +478 -0
- agentinputlab/algorithms/acquire/ircot_native.py +355 -0
- agentinputlab/algorithms/acquire/ircot_replay.py +327 -0
- agentinputlab/algorithms/acquire/qrag/__init__.py +79 -0
- agentinputlab/algorithms/acquire/qrag/bridge.py +828 -0
- agentinputlab/algorithms/acquire/qrag/common_support.py +891 -0
- agentinputlab/algorithms/acquire/qrag/native_axis.py +71 -0
- agentinputlab/algorithms/acquire/qrag/reader_adapter.py +139 -0
- agentinputlab/algorithms/acquire/r2ag/__init__.py +57 -0
- agentinputlab/algorithms/acquire/r2ag/bridge.py +1312 -0
- agentinputlab/algorithms/acquire/r2ag/common_support.py +1029 -0
- agentinputlab/algorithms/acquire/r2ag/socket_runtime.py +255 -0
- agentinputlab/algorithms/clarify/__init__.py +61 -0
- agentinputlab/algorithms/clarify/_structured.py +64 -0
- agentinputlab/algorithms/clarify/active_task_disambiguation.py +371 -0
- agentinputlab/algorithms/clarify/ask_or_assume/__init__.py +17 -0
- agentinputlab/algorithms/clarify/ask_or_assume/monitor.py +410 -0
- agentinputlab/algorithms/clarify/future_turns.py +379 -0
- agentinputlab/algorithms/clarify/udcs.py +255 -0
- agentinputlab/algorithms/clarify/udcs_deterministic.py +196 -0
- agentinputlab/algorithms/curate/__init__.py +65 -0
- agentinputlab/algorithms/curate/acon/__init__.py +27 -0
- agentinputlab/algorithms/curate/acon/common.py +284 -0
- agentinputlab/algorithms/curate/acon/common_support.py +136 -0
- agentinputlab/algorithms/curate/acon/history_bridge.py +309 -0
- agentinputlab/algorithms/curate/acon/observation_bridge.py +290 -0
- agentinputlab/algorithms/curate/llmlingua/__init__.py +30 -0
- agentinputlab/algorithms/curate/llmlingua/adapters.py +537 -0
- agentinputlab/algorithms/curate/llmlingua/budget.py +48 -0
- agentinputlab/algorithms/curate/llmlingua/controlled.py +175 -0
- agentinputlab/algorithms/curate/llmlingua/multilingual.py +105 -0
- agentinputlab/algorithms/curate/llmlingua/span_map.py +167 -0
- agentinputlab/algorithms/curate/recomp/__init__.py +47 -0
- agentinputlab/algorithms/curate/recomp/external.py +1129 -0
- agentinputlab/algorithms/curate/recomp/selective.py +304 -0
- agentinputlab/algorithms/decompose/__init__.py +107 -0
- agentinputlab/algorithms/decompose/decomp/__init__.py +53 -0
- agentinputlab/algorithms/decompose/decomp/common_support.py +478 -0
- agentinputlab/algorithms/decompose/decomp/planner_component.py +199 -0
- agentinputlab/algorithms/decompose/decomp/worker_bridge.py +416 -0
- agentinputlab/algorithms/decompose/llm_compiler/__init__.py +35 -0
- agentinputlab/algorithms/decompose/llm_compiler/bridge.py +520 -0
- agentinputlab/algorithms/decompose/llm_compiler/common_support.py +504 -0
- agentinputlab/algorithms/decompose/plan_and_budget/__init__.py +43 -0
- agentinputlab/algorithms/decompose/plan_and_budget/cached_soft_budget.py +362 -0
- agentinputlab/algorithms/decompose/plan_and_budget/decomposer_component.py +107 -0
- agentinputlab/algorithms/decompose/plan_and_budget/full.py +205 -0
- agentinputlab/algorithms/decompose/plan_and_budget/hard_cap.py +246 -0
- agentinputlab/algorithms/decompose/plan_and_budget/regenerated_soft_budget.py +448 -0
- agentinputlab/algorithms/decompose/plan_and_solve/__init__.py +33 -0
- agentinputlab/algorithms/decompose/plan_and_solve/plan_component.py +192 -0
- agentinputlab/algorithms/decompose/plan_and_solve/ps_prompt.py +127 -0
- agentinputlab/algorithms/decompose/self_ask/__init__.py +27 -0
- agentinputlab/algorithms/decompose/self_ask/loop.py +362 -0
- agentinputlab/algorithms/decompose/self_ask/planner_component.py +117 -0
- agentinputlab/algorithms/reformulate/__init__.py +85 -0
- agentinputlab/algorithms/reformulate/infocqr/__init__.py +40 -0
- agentinputlab/algorithms/reformulate/infocqr/cache_replay.py +362 -0
- agentinputlab/algorithms/reformulate/infocqr/external.py +675 -0
- agentinputlab/algorithms/reformulate/infocqr/student.py +189 -0
- agentinputlab/algorithms/reformulate/query2doc/__init__.py +42 -0
- agentinputlab/algorithms/reformulate/query2doc/controlled_live.py +190 -0
- agentinputlab/algorithms/reformulate/query2doc/models.py +95 -0
- agentinputlab/algorithms/reformulate/query2doc/official_cached.py +335 -0
- agentinputlab/algorithms/reformulate/query2doc/querygym_bridge.py +223 -0
- agentinputlab/algorithms/reformulate/query2doc/querygym_runtime.py +326 -0
- agentinputlab/algorithms/reformulate/reformer/__init__.py +23 -0
- agentinputlab/algorithms/reformulate/reformer/external.py +417 -0
- agentinputlab/algorithms/reformulate/rrr.py +696 -0
- agentinputlab/annotation/__init__.py +5 -0
- agentinputlab/annotation/independent.py +428 -0
- agentinputlab/annotation/server.py +233 -0
- agentinputlab/annotation/static/app.js +121 -0
- agentinputlab/annotation/static/index.html +60 -0
- agentinputlab/annotation/static/review.css +2 -0
- agentinputlab/annotation/static/review.html +10 -0
- agentinputlab/annotation/static/review.js +11 -0
- agentinputlab/annotation/static/styles.css +55 -0
- agentinputlab/annotation/store.py +444 -0
- agentinputlab/api.py +368 -0
- agentinputlab/artifacts/__init__.py +15 -0
- agentinputlab/artifacts/json_file.py +104 -0
- agentinputlab/artifacts/manifest.py +98 -0
- agentinputlab/artifacts/store.py +157 -0
- agentinputlab/backends/__init__.py +98 -0
- agentinputlab/backends/embedding/__init__.py +25 -0
- agentinputlab/backends/embedding/base.py +145 -0
- agentinputlab/backends/embedding/callable.py +75 -0
- agentinputlab/backends/embedding/replay.py +129 -0
- agentinputlab/backends/llm/__init__.py +31 -0
- agentinputlab/backends/llm/base.py +149 -0
- agentinputlab/backends/llm/callable.py +108 -0
- agentinputlab/backends/llm/decomp_completion.py +88 -0
- agentinputlab/backends/llm/final_content_stop.py +72 -0
- agentinputlab/backends/llm/gpt_oss_final_channel.py +84 -0
- agentinputlab/backends/llm/openai_compatible.py +332 -0
- agentinputlab/backends/llm/transformers.py +171 -0
- agentinputlab/backends/retrieval/__init__.py +35 -0
- agentinputlab/backends/retrieval/base.py +50 -0
- agentinputlab/backends/retrieval/dense.py +777 -0
- agentinputlab/backends/retrieval/memory.py +119 -0
- agentinputlab/backends/retrieval/sqlite_fts.py +155 -0
- agentinputlab/backends/user/__init__.py +15 -0
- agentinputlab/backends/user/base.py +66 -0
- agentinputlab/backends/user/callable.py +54 -0
- agentinputlab/backends/user/replay.py +89 -0
- agentinputlab/backends/vector.py +38 -0
- agentinputlab/benchmarks/__init__.py +246 -0
- agentinputlab/benchmarks/acquisition.py +106 -0
- agentinputlab/benchmarks/ale.py +540 -0
- agentinputlab/benchmarks/appworld_ul.py +669 -0
- agentinputlab/benchmarks/assets.py +226 -0
- agentinputlab/benchmarks/base.py +68 -0
- agentinputlab/benchmarks/beir_archive.py +235 -0
- agentinputlab/benchmarks/beir_sample.py +269 -0
- agentinputlab/benchmarks/bfcl_native.py +245 -0
- agentinputlab/benchmarks/bfcl_v3.py +607 -0
- agentinputlab/benchmarks/cache.py +218 -0
- agentinputlab/benchmarks/clariq.py +203 -0
- agentinputlab/benchmarks/data/registry.json +321 -0
- agentinputlab/benchmarks/data/smoke_assets.json +511 -0
- agentinputlab/benchmarks/data/statebench_v0_8_1_task_ids.json +475 -0
- agentinputlab/benchmarks/data/toolsandbox_165848b_scenario_names.json +1 -0
- agentinputlab/benchmarks/execution.py +1111 -0
- agentinputlab/benchmarks/interactive.py +257 -0
- agentinputlab/benchmarks/ir_query.py +204 -0
- agentinputlab/benchmarks/longbench.py +203 -0
- agentinputlab/benchmarks/paired_replay.py +479 -0
- agentinputlab/benchmarks/preprocess.py +2313 -0
- agentinputlab/benchmarks/production.py +236 -0
- agentinputlab/benchmarks/qa.py +963 -0
- agentinputlab/benchmarks/registry.py +110 -0
- agentinputlab/benchmarks/retrieval.py +459 -0
- agentinputlab/benchmarks/runtime.py +509 -0
- agentinputlab/benchmarks/statebench.py +938 -0
- agentinputlab/benchmarks/tau3.py +725 -0
- agentinputlab/benchmarks/toolsandbox.py +626 -0
- agentinputlab/benchmarks/trec_dl_sample.py +263 -0
- agentinputlab/campaign.py +654 -0
- agentinputlab/campaign_runner.py +953 -0
- agentinputlab/cli/__init__.py +6 -0
- agentinputlab/cli/main.py +933 -0
- agentinputlab/connectors/__init__.py +46 -0
- agentinputlab/connectors/knowledge/__init__.py +4 -0
- agentinputlab/connectors/tools/__init__.py +30 -0
- agentinputlab/connectors/tools/base.py +57 -0
- agentinputlab/connectors/tools/mcp.py +222 -0
- agentinputlab/connectors/tools/memory.py +41 -0
- agentinputlab/connectors/tools/openapi.py +695 -0
- agentinputlab/connectors/tools/schema.py +102 -0
- agentinputlab/contracts/__init__.py +91 -0
- agentinputlab/contracts/availability.py +24 -0
- agentinputlab/contracts/dependency.py +58 -0
- agentinputlab/contracts/enums.py +82 -0
- agentinputlab/contracts/errors.py +63 -0
- agentinputlab/contracts/evidence.py +43 -0
- agentinputlab/contracts/intent.py +110 -0
- agentinputlab/contracts/lifecycle.py +164 -0
- agentinputlab/contracts/patch.py +167 -0
- agentinputlab/contracts/serialization.py +93 -0
- agentinputlab/contracts/state.py +127 -0
- agentinputlab/contracts/trace.py +59 -0
- agentinputlab/contracts/worker.py +42 -0
- agentinputlab/core/__init__.py +7 -0
- agentinputlab/data/__init__.py +32 -0
- agentinputlab/data/checksums.py +9 -0
- agentinputlab/data/downloader.py +235 -0
- agentinputlab/data/http.py +171 -0
- agentinputlab/data/index.py +21 -0
- agentinputlab/data/overlap.py +28 -0
- agentinputlab/data/preprocess.py +32 -0
- agentinputlab/data/storage.py +57 -0
- agentinputlab/data/trec_sample.py +427 -0
- agentinputlab/evaluation/__init__.py +5 -0
- agentinputlab/evaluation/outcomes.py +32 -0
- agentinputlab/experiments/__init__.py +278 -0
- agentinputlab/experiments/analysis.py +981 -0
- agentinputlab/experiments/artifacts.py +687 -0
- agentinputlab/experiments/automatic_measurement.py +678 -0
- agentinputlab/experiments/auxiliary_matrix.py +203 -0
- agentinputlab/experiments/bindings.py +430 -0
- agentinputlab/experiments/blueprints.py +147 -0
- agentinputlab/experiments/builders/__init__.py +45 -0
- agentinputlab/experiments/builders/executable_external_payload.py +423 -0
- agentinputlab/experiments/builders/external_process_arena.py +1018 -0
- agentinputlab/experiments/builders/registry_intervention.py +463 -0
- agentinputlab/experiments/cli.py +158 -0
- agentinputlab/experiments/controls.py +340 -0
- agentinputlab/experiments/driver_guard.py +205 -0
- agentinputlab/experiments/drivers/__init__.py +89 -0
- agentinputlab/experiments/drivers/external_arena.py +2908 -0
- agentinputlab/experiments/drivers/method_process.py +662 -0
- agentinputlab/experiments/drivers/parity_process.py +1484 -0
- agentinputlab/experiments/drivers/static_production.py +1574 -0
- agentinputlab/experiments/executor.py +448 -0
- agentinputlab/experiments/loader.py +605 -0
- agentinputlab/experiments/measurement.py +675 -0
- agentinputlab/experiments/paper_analysis.py +1818 -0
- agentinputlab/experiments/parity_identity.py +605 -0
- agentinputlab/experiments/plan.py +277 -0
- agentinputlab/experiments/production_builders.py +3739 -0
- agentinputlab/experiments/readiness.py +432 -0
- agentinputlab/experiments/schema.py +514 -0
- agentinputlab/experiments/secrets.py +177 -0
- agentinputlab/experiments/stateful_analysis.py +1738 -0
- agentinputlab/experiments/transfer_analysis.py +1316 -0
- agentinputlab/experiments/unseen_analysis.py +1025 -0
- agentinputlab/integrations/__init__.py +1 -0
- agentinputlab/integrations/action_space/__init__.py +1 -0
- agentinputlab/integrations/external_workers/__init__.py +76 -0
- agentinputlab/integrations/external_workers/ace.py +431 -0
- agentinputlab/integrations/external_workers/base.py +181 -0
- agentinputlab/integrations/external_workers/decomp_live.py +249 -0
- agentinputlab/integrations/external_workers/decomp_planner.py +30 -0
- agentinputlab/integrations/external_workers/infocqr_student.py +280 -0
- agentinputlab/integrations/external_workers/ircot.py +576 -0
- agentinputlab/integrations/external_workers/llmcompiler_live.py +360 -0
- agentinputlab/integrations/external_workers/longllmlingua.py +162 -0
- agentinputlab/integrations/external_workers/method.py +314 -0
- agentinputlab/integrations/external_workers/process.py +678 -0
- agentinputlab/integrations/external_workers/supervisor.py +1071 -0
- agentinputlab/integrations/full_architecture/__init__.py +1 -0
- agentinputlab/integrations/full_architecture/hyde.py +212 -0
- agentinputlab/integrations/full_architecture/plan_and_budget.py +188 -0
- agentinputlab/integrations/live_history.py +222 -0
- agentinputlab/integrations/prompt_optimization/__init__.py +1 -0
- agentinputlab/integrations/training/__init__.py +1 -0
- agentinputlab/metrics/__init__.py +50 -0
- agentinputlab/metrics/catalog.py +313 -0
- agentinputlab/metrics/harm.py +95 -0
- agentinputlab/metrics/paired.py +40 -0
- agentinputlab/metrics/qa.py +76 -0
- agentinputlab/metrics/text.py +33 -0
- agentinputlab/metrics/third_party.py +161 -0
- agentinputlab/models/__init__.py +4 -0
- agentinputlab/observability/__init__.py +56 -0
- agentinputlab/observability/budget.py +316 -0
- agentinputlab/observability/instrumentation.py +754 -0
- agentinputlab/observability/models.py +403 -0
- agentinputlab/observability/monitor.py +380 -0
- agentinputlab/optimizers/__init__.py +1 -0
- agentinputlab/optimizers/clarify/__init__.py +12 -0
- agentinputlab/optimizers/context/__init__.py +22 -0
- agentinputlab/optimizers/decompose/__init__.py +64 -0
- agentinputlab/optimizers/evidence/__init__.py +22 -0
- agentinputlab/optimizers/rewrite/__init__.py +30 -0
- agentinputlab/parity/__init__.py +5 -0
- agentinputlab/parity/base.py +206 -0
- agentinputlab/production_campaign.py +763 -0
- agentinputlab/py.typed +1 -0
- agentinputlab/recipes/__init__.py +333 -0
- agentinputlab/recipes/ace.py +418 -0
- agentinputlab/recipes/ace_bfcl.py +273 -0
- agentinputlab/recipes/ace_common_support.py +169 -0
- agentinputlab/recipes/ace_native_plan.py +322 -0
- agentinputlab/recipes/ace_online.py +711 -0
- agentinputlab/recipes/ace_retrieval.py +330 -0
- agentinputlab/recipes/ace_statebench.py +382 -0
- agentinputlab/recipes/ace_tau.py +463 -0
- agentinputlab/recipes/ace_toolsandbox.py +432 -0
- agentinputlab/recipes/acon_guideline.py +22 -0
- agentinputlab/recipes/adaptive_rag.py +822 -0
- agentinputlab/recipes/llmlingua2_inference.py +146 -0
- agentinputlab/recipes/llmlingua2_training.py +796 -0
- agentinputlab/recipes/mce.py +1486 -0
- agentinputlab/recipes/mce_final.py +335 -0
- agentinputlab/recipes/mce_text_fit.py +499 -0
- agentinputlab/recipes/mce_text_only.py +298 -0
- agentinputlab/recipes/qrag.py +729 -0
- agentinputlab/recipes/qrag_inference.py +364 -0
- agentinputlab/recipes/r2ag_features.py +236 -0
- agentinputlab/recipes/r2ag_inference.py +503 -0
- agentinputlab/recipes/r2ag_training.py +948 -0
- agentinputlab/recipes/recomp_abstractive.py +592 -0
- agentinputlab/recipes/recomp_extractive.py +525 -0
- agentinputlab/recipes/reformer.py +17 -0
- agentinputlab/recipes/rqrag.py +1516 -0
- agentinputlab/recipes/rqrag_common_support.py +193 -0
- agentinputlab/recipes/rqrag_completion.py +147 -0
- agentinputlab/recipes/skillopt.py +462 -0
- agentinputlab/recipes/skillopt_common_support.py +154 -0
- agentinputlab/recipes/skillopt_environment.py +165 -0
- agentinputlab/recipes/skillopt_environment_cli.py +5 -0
- agentinputlab/recipes/skillopt_retrieval.py +188 -0
- agentinputlab/recipes/smartsearch.py +897 -0
- agentinputlab/recipes/smartsearch_curriculum.py +283 -0
- agentinputlab/recipes/smartsearch_grpo_contracts.py +58 -0
- agentinputlab/recipes/smartsearch_grpo_data.py +314 -0
- agentinputlab/recipes/smartsearch_grpo_meter.py +324 -0
- agentinputlab/recipes/smartsearch_grpo_prefix.py +144 -0
- agentinputlab/recipes/smartsearch_grpo_rollout.py +119 -0
- agentinputlab/recipes/smartsearch_grpo_tools.py +364 -0
- agentinputlab/recipes/smartsearch_grpo_training.py +305 -0
- agentinputlab/recipes/smartsearch_grpo_transport.py +248 -0
- agentinputlab/recipes/smartsearch_grpo_worker.py +364 -0
- agentinputlab/recipes/smartsearch_inference.py +524 -0
- agentinputlab/recipes/smartsearch_native_data.py +172 -0
- agentinputlab/recipes/smartsearch_policy.py +616 -0
- agentinputlab/recipes/smartsearch_policy_worker.py +9 -0
- agentinputlab/recipes/smartsearch_sampling.py +232 -0
- agentinputlab/recipes/smartsearch_teacher.py +203 -0
- agentinputlab/recipes/smartsearch_training.py +265 -0
- agentinputlab/registry/__init__.py +46 -0
- agentinputlab/registry/beta.py +727 -0
- agentinputlab/registry/data/algorithms.json +1959 -0
- agentinputlab/registry/data/auxiliary_method_profiles.json +34 -0
- agentinputlab/registry/loader.py +100 -0
- agentinputlab/registry/result_manifest.py +184 -0
- agentinputlab/registry/runtime.py +195 -0
- agentinputlab/registry/schema.py +449 -0
- agentinputlab/runner/__init__.py +30 -0
- agentinputlab/runner/balance.py +248 -0
- agentinputlab/runner/cache.py +135 -0
- agentinputlab/runner/composite.py +130 -0
- agentinputlab/runner/engine.py +285 -0
- agentinputlab/runner/runtime_guard.py +268 -0
- agentinputlab/runner/worker.py +80 -0
- agentinputlab/runtime_bootstrap.py +288 -0
- agentinputlab/selectors/__init__.py +196 -0
- agentinputlab/selectors/advanced.py +1514 -0
- agentinputlab/selectors/artifacts.py +1087 -0
- agentinputlab/selectors/base.py +112 -0
- agentinputlab/selectors/campaigns.py +1893 -0
- agentinputlab/selectors/catalog.py +86 -0
- agentinputlab/selectors/discovery.py +1860 -0
- agentinputlab/selectors/evaluation.py +1348 -0
- agentinputlab/selectors/features.py +383 -0
- agentinputlab/selectors/learned.py +502 -0
- agentinputlab/selectors/no_op.py +28 -0
- agentinputlab/selectors/outcome_table.py +475 -0
- agentinputlab/selectors/producer.py +853 -0
- agentinputlab/selectors/rules.py +109 -0
- agentinputlab/selectors/runtime.py +420 -0
- agentinputlab/studies/__init__.py +16 -0
- agentinputlab/studies/design.py +165 -0
- agentinputlab/studies/paired.py +322 -0
- agentinputlab/studio/__init__.py +6 -0
- agentinputlab/studio/campaigns.py +819 -0
- agentinputlab/studio/engine.py +1408 -0
- agentinputlab/studio/model_backends.py +425 -0
- agentinputlab/studio/prepared.py +410 -0
- agentinputlab/studio/server.py +613 -0
- agentinputlab/studio/static/app.js +397 -0
- agentinputlab/studio/static/campaigns.html +175 -0
- agentinputlab/studio/static/campaigns.js +426 -0
- agentinputlab/studio/static/index.html +192 -0
- agentinputlab/studio/static/result.html +103 -0
- agentinputlab/studio/static/result.js +273 -0
- agentinputlab/studio/static/styles.css +467 -0
- agentinputlab-0.1.0a1.data/data/share/agentinputlab/CITATION.cff +22 -0
- agentinputlab-0.1.0a1.dist-info/METADATA +392 -0
- agentinputlab-0.1.0a1.dist-info/RECORD +469 -0
- agentinputlab-0.1.0a1.dist-info/WHEEL +5 -0
- agentinputlab-0.1.0a1.dist-info/entry_points.txt +6 -0
- agentinputlab-0.1.0a1.dist-info/licenses/LICENSE +21 -0
- agentinputlab-0.1.0a1.dist-info/licenses/NOTICE +31 -0
- agentinputlab-0.1.0a1.dist-info/licenses/THIRD_PARTY_NOTICES.md +94 -0
- agentinputlab-0.1.0a1.dist-info/top_level.txt +2 -0
- workers/README.md +94 -0
- workers/ace/README.md +261 -0
- workers/ace/__init__.py +1 -0
- workers/ace/compatibility_manifest.json +39 -0
- workers/ace/driver.py +1682 -0
- workers/acon/README.md +215 -0
- workers/acon/__init__.py +5 -0
- workers/acon/compatibility_manifest.json +93 -0
- workers/acon/driver.py +1859 -0
- workers/acon/fitted.py +395 -0
- workers/acon/guideline.py +470 -0
- workers/acon/guideline_source_manifest.json +20 -0
- workers/adaptive_rag/README.md +96 -0
- workers/adaptive_rag/__init__.py +15 -0
- workers/adaptive_rag/compatibility_manifest.json +61 -0
- workers/adaptive_rag/driver.py +1470 -0
- workers/adaptive_rag/fixture_runtime.py +163 -0
- workers/adaptive_rag/runtime.py +252 -0
- workers/ask_or_assume/README.md +124 -0
- workers/ask_or_assume/__init__.py +1 -0
- workers/ask_or_assume/compatibility_manifest.json +36 -0
- workers/ask_or_assume/driver.py +1227 -0
- workers/decomp/README.md +133 -0
- workers/decomp/UPSTREAM_LICENSE.Apache-2.0.txt +201 -0
- workers/decomp/__init__.py +5 -0
- workers/decomp/compatibility_manifest.json +205 -0
- workers/decomp/driver.py +1015 -0
- workers/decomp/live.py +137 -0
- workers/decomp/runtime.py +231 -0
- workers/infocqr/README.md +106 -0
- workers/infocqr/__init__.py +1 -0
- workers/infocqr/compatibility_manifest.json +84 -0
- workers/infocqr/driver.py +1766 -0
- workers/ircot/README.md +167 -0
- workers/ircot/__init__.py +7 -0
- workers/ircot/compatibility_manifest.json +121 -0
- workers/ircot/controlled.py +457 -0
- workers/ircot/driver.py +1373 -0
- workers/ircot/fixture_bindings.json +39 -0
- workers/ircot/fixture_config.json +48 -0
- workers/ircot/fixture_prompt.txt +2 -0
- workers/ircot/native_qa.py +398 -0
- workers/ircot/qa_compatibility_manifest.json +57 -0
- workers/ircot/runtime.py +97 -0
- workers/llmcompiler/README.md +160 -0
- workers/llmcompiler/__init__.py +1 -0
- workers/llmcompiler/compatibility_manifest.json +45 -0
- workers/llmcompiler/driver.py +1514 -0
- workers/llmcompiler/export_prompts.py +74 -0
- workers/llmcompiler/live.py +174 -0
- workers/llmcompiler/patch_manifest.json +147 -0
- workers/mce/README.md +198 -0
- workers/mce/__init__.py +29 -0
- workers/mce/compatibility_manifest.json +115 -0
- workers/mce/driver.py +2585 -0
- workers/mce/final_eval.py +173 -0
- workers/official_arenas/README.md +25 -0
- workers/official_arenas/__init__.py +1 -0
- workers/official_arenas/capture_records.py +110 -0
- workers/official_arenas/driver.py +619 -0
- workers/official_arenas/protocol.py +334 -0
- workers/official_arenas/providers/__init__.py +1 -0
- workers/official_arenas/providers/_live_history.py +228 -0
- workers/official_arenas/providers/_message_compat.py +77 -0
- workers/official_arenas/providers/appworld_ul.py +196 -0
- workers/official_arenas/providers/fixture.py +112 -0
- workers/official_arenas/providers/statebench.py +960 -0
- workers/official_arenas/providers/tau3.py +1672 -0
- workers/official_arenas/providers/tau_retail.py +586 -0
- workers/official_arenas/providers/toolsandbox.py +1321 -0
- workers/official_arenas/providers/tooltalk.py +699 -0
- workers/official_arenas/retail_capture.py +188 -0
- workers/official_arenas/retail_driver.py +275 -0
- workers/qrag/README.md +172 -0
- workers/qrag/__init__.py +1 -0
- workers/qrag/compatibility_manifest.json +64 -0
- workers/qrag/driver.py +673 -0
- workers/r2ag/README.md +154 -0
- workers/r2ag/__init__.py +5 -0
- workers/r2ag/compatibility_manifest.json +55 -0
- workers/r2ag/driver.py +1287 -0
- workers/r2ag/feature_schema.json +8 -0
- workers/r2ag/runtime.py +589 -0
- workers/recomp/README.md +120 -0
- workers/recomp/__init__.py +1 -0
- workers/recomp/compatibility_manifest.json +43 -0
- workers/recomp/driver.py +1653 -0
- workers/reformer/README.md +125 -0
- workers/reformer/__init__.py +1 -0
- workers/reformer/compatibility_manifest.json +68 -0
- workers/reformer/driver.py +1459 -0
- workers/reformer/induction.py +457 -0
- workers/rrr/README.md +126 -0
- workers/rrr/__init__.py +1 -0
- workers/rrr/compatibility_manifest.json +57 -0
- workers/rrr/driver.py +1133 -0
- workers/smartsearch/README.md +331 -0
- workers/smartsearch/__init__.py +1 -0
- workers/smartsearch/compatibility_manifest.json +41 -0
- 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
|
+
]
|