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,332 @@
|
|
|
1
|
+
"""OpenAI-compatible backend for hosted APIs and self-hosted vLLM servers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import http.client
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import urllib.error
|
|
9
|
+
import urllib.parse
|
|
10
|
+
import urllib.request
|
|
11
|
+
from collections.abc import Callable, Mapping
|
|
12
|
+
from contextlib import suppress
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from agentinputlab.contracts.errors import CapabilityError, ValidationError
|
|
17
|
+
from agentinputlab.contracts.serialization import content_hash, freeze_mapping, to_jsonable
|
|
18
|
+
|
|
19
|
+
from .base import (
|
|
20
|
+
GenerationRequest,
|
|
21
|
+
GenerationResponse,
|
|
22
|
+
LLMBackend,
|
|
23
|
+
ModelCapability,
|
|
24
|
+
Usage,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
Transport = Callable[[str, Mapping[str, str], Mapping[str, Any], float], Mapping[str, Any]]
|
|
28
|
+
_MAX_RESPONSE_BYTES = 16 * 1024 * 1024
|
|
29
|
+
_SENSITIVE_EXTRA_HEADERS = frozenset(
|
|
30
|
+
{
|
|
31
|
+
"api-key",
|
|
32
|
+
"authorization",
|
|
33
|
+
"cookie",
|
|
34
|
+
"proxy-authorization",
|
|
35
|
+
"set-cookie",
|
|
36
|
+
"x-amz-security-token",
|
|
37
|
+
"x-api-key",
|
|
38
|
+
"x-auth-token",
|
|
39
|
+
"x-goog-api-key",
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
_TRANSPORT_ATTEMPT_POLICY = "single_transport_no_implicit_retry.v1"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class _OpenAICompatibleHTTPStatusError(RuntimeError):
|
|
46
|
+
"""Sanitized status-only failure without remote response content."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, status: int) -> None:
|
|
49
|
+
self.status = status
|
|
50
|
+
super().__init__(f"OpenAI-compatible endpoint returned HTTP {status}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class _NoRedirectHandler(urllib.request.HTTPRedirectHandler):
|
|
54
|
+
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _default_transport(
|
|
59
|
+
url: str,
|
|
60
|
+
headers: Mapping[str, str],
|
|
61
|
+
payload: Mapping[str, Any],
|
|
62
|
+
timeout_seconds: float,
|
|
63
|
+
) -> Mapping[str, Any]:
|
|
64
|
+
body = json.dumps(payload).encode("utf-8")
|
|
65
|
+
request = urllib.request.Request(url, data=body, headers=dict(headers), method="POST")
|
|
66
|
+
opener = urllib.request.build_opener(_NoRedirectHandler())
|
|
67
|
+
http_status: int | None = None
|
|
68
|
+
endpoint_unavailable = False
|
|
69
|
+
protocol_failure = False
|
|
70
|
+
try:
|
|
71
|
+
with opener.open(request, timeout=timeout_seconds) as response:
|
|
72
|
+
raw = response.read(_MAX_RESPONSE_BYTES + 1)
|
|
73
|
+
if len(raw) > _MAX_RESPONSE_BYTES:
|
|
74
|
+
raise RuntimeError("OpenAI-compatible response exceeds the size limit")
|
|
75
|
+
return json.loads(raw.decode("utf-8"))
|
|
76
|
+
except urllib.error.HTTPError as exc:
|
|
77
|
+
# A remote endpoint can reflect Authorization values or other opaque
|
|
78
|
+
# request material in its error body. Never copy that untrusted body
|
|
79
|
+
# into exceptions because experiment monitors may persist them.
|
|
80
|
+
http_status = exc.code
|
|
81
|
+
# Cleanup diagnostics are remote/transport-controlled too; the fixed
|
|
82
|
+
# status-only error below remains the sole public message.
|
|
83
|
+
with suppress(Exception):
|
|
84
|
+
exc.close()
|
|
85
|
+
except urllib.error.URLError:
|
|
86
|
+
# ``reason`` is likewise remote/transport-controlled and may contain a
|
|
87
|
+
# credential-bearing URL. Keep the diagnostic intentionally generic.
|
|
88
|
+
endpoint_unavailable = True
|
|
89
|
+
except http.client.HTTPException:
|
|
90
|
+
# Invalid status lines and incomplete HTTP frames can embed raw,
|
|
91
|
+
# remote-controlled bytes in their exception text.
|
|
92
|
+
protocol_failure = True
|
|
93
|
+
# Raise after leaving the handler so neither ``__cause__`` nor
|
|
94
|
+
# ``__context__`` retains the remote-controlled exception for traceback
|
|
95
|
+
# loggers to render.
|
|
96
|
+
if http_status is not None:
|
|
97
|
+
raise _OpenAICompatibleHTTPStatusError(http_status)
|
|
98
|
+
if endpoint_unavailable:
|
|
99
|
+
raise RuntimeError("OpenAI-compatible endpoint is unavailable")
|
|
100
|
+
if protocol_failure:
|
|
101
|
+
raise RuntimeError("OpenAI-compatible endpoint returned an invalid HTTP response")
|
|
102
|
+
raise RuntimeError("OpenAI-compatible transport failed without a diagnostic")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(slots=True)
|
|
106
|
+
class OpenAICompatibleBackend(LLMBackend):
|
|
107
|
+
model_id: str
|
|
108
|
+
base_url: str
|
|
109
|
+
deployment_identity: Mapping[str, Any] = field(default_factory=dict)
|
|
110
|
+
api_key: str | None = field(default=None, repr=False)
|
|
111
|
+
api_key_env: str = "OPENAI_API_KEY"
|
|
112
|
+
timeout_seconds: float = 120.0
|
|
113
|
+
extra_headers: Mapping[str, str] = field(default_factory=dict, repr=False)
|
|
114
|
+
extra_body: Mapping[str, Any] = field(default_factory=dict, repr=False)
|
|
115
|
+
capabilities: frozenset[ModelCapability] = frozenset(
|
|
116
|
+
{
|
|
117
|
+
ModelCapability.CHAT,
|
|
118
|
+
ModelCapability.COMPLETION,
|
|
119
|
+
ModelCapability.JSON_OBJECT,
|
|
120
|
+
ModelCapability.TOOL_CALLING,
|
|
121
|
+
ModelCapability.SEED,
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
transport: Transport = _default_transport
|
|
125
|
+
|
|
126
|
+
def __post_init__(self) -> None:
|
|
127
|
+
parsed = urllib.parse.urlparse(self.base_url)
|
|
128
|
+
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
|
129
|
+
raise ValidationError("base_url must be an absolute http(s) URL")
|
|
130
|
+
if parsed.username or parsed.password or parsed.query or parsed.fragment:
|
|
131
|
+
raise ValidationError("base_url must not contain credentials, query, or fragment")
|
|
132
|
+
if not self.deployment_identity or not self.deployment_identity.get("revision"):
|
|
133
|
+
raise ValidationError("OpenAI-compatible backend requires deployment_identity.revision")
|
|
134
|
+
for name, value in self.extra_headers.items():
|
|
135
|
+
if not isinstance(name, str) or not isinstance(value, str):
|
|
136
|
+
raise ValidationError("OpenAI-compatible extra headers must contain strings")
|
|
137
|
+
if name.casefold() in _SENSITIVE_EXTRA_HEADERS:
|
|
138
|
+
raise ValidationError(
|
|
139
|
+
f"sensitive extra header {name!r} is forbidden; use api_key or api_key_env"
|
|
140
|
+
)
|
|
141
|
+
self.base_url = self.base_url.rstrip("/")
|
|
142
|
+
self.deployment_identity = freeze_mapping(self.deployment_identity)
|
|
143
|
+
self.extra_headers = freeze_mapping(self.extra_headers)
|
|
144
|
+
self.extra_body = freeze_mapping(self.extra_body)
|
|
145
|
+
self.capabilities = frozenset(self.capabilities)
|
|
146
|
+
|
|
147
|
+
def generate(self, request: GenerationRequest) -> GenerationResponse:
|
|
148
|
+
if request.tools and ModelCapability.TOOL_CALLING not in self.capabilities:
|
|
149
|
+
raise CapabilityError(f"backend {self.model_id!r} does not support tools")
|
|
150
|
+
if request.seed is not None and ModelCapability.SEED not in self.capabilities:
|
|
151
|
+
raise CapabilityError(f"backend {self.model_id!r} does not support seed")
|
|
152
|
+
if request.logprobs and ModelCapability.LOGPROBS not in self.capabilities:
|
|
153
|
+
raise CapabilityError(f"backend {self.model_id!r} does not support logprobs")
|
|
154
|
+
if (
|
|
155
|
+
request.response_format
|
|
156
|
+
and ModelCapability.JSON_OBJECT not in self.capabilities
|
|
157
|
+
and ModelCapability.JSON_SCHEMA not in self.capabilities
|
|
158
|
+
):
|
|
159
|
+
raise CapabilityError(f"backend {self.model_id!r} does not support response_format")
|
|
160
|
+
messages: list[dict[str, str]] = []
|
|
161
|
+
if request.system:
|
|
162
|
+
messages.append({"role": "system", "content": request.system})
|
|
163
|
+
if request.messages:
|
|
164
|
+
for message in request.messages:
|
|
165
|
+
item = {"role": message.role, "content": message.content}
|
|
166
|
+
if message.name:
|
|
167
|
+
item["name"] = message.name
|
|
168
|
+
messages.append(item)
|
|
169
|
+
elif request.prompt:
|
|
170
|
+
messages.append({"role": "user", "content": request.prompt})
|
|
171
|
+
|
|
172
|
+
payload: dict[str, Any] = {
|
|
173
|
+
**dict(self.extra_body),
|
|
174
|
+
"model": self.model_id,
|
|
175
|
+
"messages": messages,
|
|
176
|
+
"temperature": request.temperature,
|
|
177
|
+
"max_tokens": request.max_tokens,
|
|
178
|
+
}
|
|
179
|
+
if request.seed is not None:
|
|
180
|
+
payload["seed"] = request.seed
|
|
181
|
+
if request.stop:
|
|
182
|
+
payload["stop"] = list(request.stop)
|
|
183
|
+
if request.logprobs:
|
|
184
|
+
payload["logprobs"] = True
|
|
185
|
+
if request.top_logprobs is not None:
|
|
186
|
+
payload["top_logprobs"] = request.top_logprobs
|
|
187
|
+
if request.response_format:
|
|
188
|
+
payload["response_format"] = dict(request.response_format)
|
|
189
|
+
if request.tools:
|
|
190
|
+
payload["tools"] = list(request.tools)
|
|
191
|
+
|
|
192
|
+
# ``freeze_mapping`` recursively protects backend/request contracts,
|
|
193
|
+
# so nested values may be MappingProxyType instances. Materialize the
|
|
194
|
+
# complete payload—not just its outer mapping—before handing it to a
|
|
195
|
+
# custom transport or ``json.dumps``.
|
|
196
|
+
payload = dict(to_jsonable(payload))
|
|
197
|
+
|
|
198
|
+
key = self.api_key or os.environ.get(self.api_key_env, "")
|
|
199
|
+
headers = {"Content-Type": "application/json", **dict(self.extra_headers)}
|
|
200
|
+
if key:
|
|
201
|
+
headers["Authorization"] = f"Bearer {key}"
|
|
202
|
+
data: Mapping[str, Any] | None = None
|
|
203
|
+
transport_failed = False
|
|
204
|
+
try:
|
|
205
|
+
data = self.transport(
|
|
206
|
+
f"{self.base_url}/chat/completions",
|
|
207
|
+
headers,
|
|
208
|
+
payload,
|
|
209
|
+
self.timeout_seconds,
|
|
210
|
+
)
|
|
211
|
+
except Exception:
|
|
212
|
+
# Custom transports are an extension boundary and may raise
|
|
213
|
+
# exceptions containing request headers or provider bodies. Do
|
|
214
|
+
# not let their text or traceback context enter run artifacts.
|
|
215
|
+
transport_failed = True
|
|
216
|
+
if transport_failed:
|
|
217
|
+
# A failed request may already have generated/billed tokens. Do
|
|
218
|
+
# not hide another model attempt inside one budget reservation,
|
|
219
|
+
# or replace its unknown usage with a successful retry's usage.
|
|
220
|
+
raise RuntimeError("OpenAI-compatible transport failed")
|
|
221
|
+
if key and _contains_exact_text(data, key):
|
|
222
|
+
raise RuntimeError("OpenAI-compatible response contains the configured credential")
|
|
223
|
+
response: GenerationResponse | None = None
|
|
224
|
+
schema_error = False
|
|
225
|
+
try:
|
|
226
|
+
response = _parse_generation_response(data, fallback_model=self.model_id)
|
|
227
|
+
except (AttributeError, IndexError, KeyError, OverflowError, TypeError, ValueError):
|
|
228
|
+
schema_error = True
|
|
229
|
+
# Leave the handler before raising so remote-controlled values cannot
|
|
230
|
+
# survive in an exception context rendered by a traceback logger.
|
|
231
|
+
if schema_error or response is None:
|
|
232
|
+
raise RuntimeError("OpenAI-compatible response schema is invalid")
|
|
233
|
+
return response
|
|
234
|
+
|
|
235
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
236
|
+
parsed = urllib.parse.urlparse(self.base_url)
|
|
237
|
+
identity = {
|
|
238
|
+
"backend": "openai_compatible",
|
|
239
|
+
"model_id": self.model_id,
|
|
240
|
+
"endpoint_origin": f"{parsed.scheme}://{parsed.netloc}",
|
|
241
|
+
"base_url": self.base_url,
|
|
242
|
+
"deployment_identity_hash": content_hash(self.deployment_identity),
|
|
243
|
+
"capabilities": sorted(item.value for item in self.capabilities),
|
|
244
|
+
"extra_body_hash": content_hash(self.extra_body),
|
|
245
|
+
"extra_header_names": sorted(self.extra_headers),
|
|
246
|
+
"extra_headers_hash": content_hash(self.extra_headers),
|
|
247
|
+
"transport_attempt_policy": _TRANSPORT_ATTEMPT_POLICY,
|
|
248
|
+
}
|
|
249
|
+
revision = self.deployment_identity.get("revision")
|
|
250
|
+
if isinstance(revision, str) and revision.strip():
|
|
251
|
+
# Source-bound workers need the declared public revision as well as
|
|
252
|
+
# its deployment hash. Never expose the entire operator mapping.
|
|
253
|
+
identity["model_revision"] = revision
|
|
254
|
+
return identity
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _parse_generation_response(
|
|
258
|
+
data: Any,
|
|
259
|
+
*,
|
|
260
|
+
fallback_model: str,
|
|
261
|
+
) -> GenerationResponse:
|
|
262
|
+
if not isinstance(data, Mapping):
|
|
263
|
+
raise TypeError("response must be a mapping")
|
|
264
|
+
choices = data.get("choices")
|
|
265
|
+
if not isinstance(choices, list) or not choices:
|
|
266
|
+
raise ValueError("response choices must be a non-empty list")
|
|
267
|
+
choice = choices[0]
|
|
268
|
+
if not isinstance(choice, Mapping):
|
|
269
|
+
raise TypeError("response choice must be a mapping")
|
|
270
|
+
message = choice.get("message")
|
|
271
|
+
text: Any = None
|
|
272
|
+
if message is not None:
|
|
273
|
+
if not isinstance(message, Mapping):
|
|
274
|
+
raise TypeError("response message must be a mapping")
|
|
275
|
+
text = message.get("content")
|
|
276
|
+
if text is None:
|
|
277
|
+
text = choice.get("text", "")
|
|
278
|
+
if not isinstance(text, str):
|
|
279
|
+
raise TypeError("response text must be a string")
|
|
280
|
+
model = data.get("model", fallback_model)
|
|
281
|
+
if not isinstance(model, str):
|
|
282
|
+
raise TypeError("response model must be a string")
|
|
283
|
+
finish_reason = choice.get("finish_reason", "")
|
|
284
|
+
if finish_reason is None:
|
|
285
|
+
finish_reason = ""
|
|
286
|
+
if not isinstance(finish_reason, str):
|
|
287
|
+
raise TypeError("response finish_reason must be a string")
|
|
288
|
+
usage = data.get("usage", {})
|
|
289
|
+
if usage is None:
|
|
290
|
+
usage = {}
|
|
291
|
+
if not isinstance(usage, Mapping):
|
|
292
|
+
raise TypeError("response usage must be a mapping")
|
|
293
|
+
return GenerationResponse(
|
|
294
|
+
text=text,
|
|
295
|
+
model=model,
|
|
296
|
+
finish_reason=finish_reason,
|
|
297
|
+
usage=Usage(
|
|
298
|
+
input_tokens=_response_token_count(usage.get("prompt_tokens", 0)),
|
|
299
|
+
output_tokens=_response_token_count(usage.get("completion_tokens", 0)),
|
|
300
|
+
),
|
|
301
|
+
raw=data,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _response_token_count(value: Any) -> int:
|
|
306
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
307
|
+
raise ValueError("response token count must be a non-negative integer")
|
|
308
|
+
return value
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _contains_exact_text(value: Any, needle: str, *, _seen: set[int] | None = None) -> bool:
|
|
312
|
+
if isinstance(value, str):
|
|
313
|
+
return needle in value
|
|
314
|
+
if isinstance(value, Mapping):
|
|
315
|
+
seen = set() if _seen is None else _seen
|
|
316
|
+
identity = id(value)
|
|
317
|
+
if identity in seen:
|
|
318
|
+
return False
|
|
319
|
+
seen.add(identity)
|
|
320
|
+
return any(
|
|
321
|
+
(isinstance(key, str) and needle in key)
|
|
322
|
+
or _contains_exact_text(item, needle, _seen=seen)
|
|
323
|
+
for key, item in value.items()
|
|
324
|
+
)
|
|
325
|
+
if isinstance(value, (list, tuple)):
|
|
326
|
+
seen = set() if _seen is None else _seen
|
|
327
|
+
identity = id(value)
|
|
328
|
+
if identity in seen:
|
|
329
|
+
return False
|
|
330
|
+
seen.add(identity)
|
|
331
|
+
return any(_contains_exact_text(item, needle, _seen=seen) for item in value)
|
|
332
|
+
return False
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Optional local Transformers backend loaded without a hard runtime dependency."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from agentinputlab.contracts.errors import CapabilityError, ExternalDependencyError, ValidationError
|
|
10
|
+
from agentinputlab.contracts.serialization import freeze_mapping
|
|
11
|
+
|
|
12
|
+
from .base import GenerationRequest, GenerationResponse, LLMBackend, ModelCapability, Usage
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(slots=True)
|
|
16
|
+
class TransformersBackend(LLMBackend):
|
|
17
|
+
model_id: str
|
|
18
|
+
model_revision: str
|
|
19
|
+
tokenizer_revision: str
|
|
20
|
+
tokenizer: Any = None
|
|
21
|
+
model: Any = None
|
|
22
|
+
generation_kwargs: Mapping[str, Any] = field(default_factory=dict)
|
|
23
|
+
cache_dir: str | None = None
|
|
24
|
+
offline: bool = False
|
|
25
|
+
capabilities: frozenset[ModelCapability] = frozenset(
|
|
26
|
+
{ModelCapability.CHAT, ModelCapability.COMPLETION}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def __post_init__(self) -> None:
|
|
30
|
+
if not isinstance(self.offline, bool):
|
|
31
|
+
raise ValidationError("TransformersBackend offline must be boolean")
|
|
32
|
+
if self.cache_dir is not None and (
|
|
33
|
+
not isinstance(self.cache_dir, str) or not self.cache_dir.strip()
|
|
34
|
+
):
|
|
35
|
+
raise ValidationError("TransformersBackend cache_dir must be a non-empty path string")
|
|
36
|
+
if not self.model_revision or not self.tokenizer_revision:
|
|
37
|
+
raise ValidationError(
|
|
38
|
+
"TransformersBackend requires explicit model and tokenizer revisions"
|
|
39
|
+
)
|
|
40
|
+
self.generation_kwargs = freeze_mapping(self.generation_kwargs)
|
|
41
|
+
num_return_sequences = self.generation_kwargs.get("num_return_sequences", 1)
|
|
42
|
+
if (
|
|
43
|
+
isinstance(num_return_sequences, bool)
|
|
44
|
+
or not isinstance(num_return_sequences, int)
|
|
45
|
+
or num_return_sequences != 1
|
|
46
|
+
):
|
|
47
|
+
raise ValidationError(
|
|
48
|
+
"TransformersBackend requires num_return_sequences to be the integer 1"
|
|
49
|
+
)
|
|
50
|
+
self.capabilities = frozenset(self.capabilities)
|
|
51
|
+
|
|
52
|
+
def _load(self) -> None:
|
|
53
|
+
if self.model is not None and self.tokenizer is not None:
|
|
54
|
+
return
|
|
55
|
+
try:
|
|
56
|
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
57
|
+
except ImportError as exc:
|
|
58
|
+
raise ExternalDependencyError(
|
|
59
|
+
"TransformersBackend requires `pip install agentinputlab[local]`"
|
|
60
|
+
) from exc
|
|
61
|
+
self.tokenizer = AutoTokenizer.from_pretrained(
|
|
62
|
+
self.model_id,
|
|
63
|
+
revision=self.tokenizer_revision,
|
|
64
|
+
trust_remote_code=False,
|
|
65
|
+
cache_dir=self.cache_dir,
|
|
66
|
+
local_files_only=self.offline,
|
|
67
|
+
)
|
|
68
|
+
self.model = AutoModelForCausalLM.from_pretrained(
|
|
69
|
+
self.model_id,
|
|
70
|
+
revision=self.model_revision,
|
|
71
|
+
trust_remote_code=False,
|
|
72
|
+
cache_dir=self.cache_dir,
|
|
73
|
+
local_files_only=self.offline,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def generate(self, request: GenerationRequest) -> GenerationResponse:
|
|
77
|
+
if request.seed is not None and ModelCapability.SEED not in self.capabilities:
|
|
78
|
+
raise CapabilityError(
|
|
79
|
+
"TransformersBackend does not implement deterministic seed control"
|
|
80
|
+
)
|
|
81
|
+
if request.tools and ModelCapability.TOOL_CALLING not in self.capabilities:
|
|
82
|
+
raise CapabilityError("TransformersBackend does not support structured tool calling")
|
|
83
|
+
if request.response_format and not {
|
|
84
|
+
ModelCapability.JSON_OBJECT,
|
|
85
|
+
ModelCapability.JSON_SCHEMA,
|
|
86
|
+
}.intersection(self.capabilities):
|
|
87
|
+
raise CapabilityError("TransformersBackend does not support response_format")
|
|
88
|
+
self._load()
|
|
89
|
+
if request.messages and hasattr(self.tokenizer, "apply_chat_template"):
|
|
90
|
+
messages = []
|
|
91
|
+
if request.system:
|
|
92
|
+
messages.append({"role": "system", "content": request.system})
|
|
93
|
+
messages.extend(
|
|
94
|
+
{"role": item.role, "content": item.content} for item in request.messages
|
|
95
|
+
)
|
|
96
|
+
prompt = self.tokenizer.apply_chat_template(
|
|
97
|
+
messages,
|
|
98
|
+
tokenize=False,
|
|
99
|
+
add_generation_prompt=True,
|
|
100
|
+
)
|
|
101
|
+
elif request.messages:
|
|
102
|
+
raise CapabilityError(
|
|
103
|
+
"Transformers tokenizer has no chat template for message-based generation"
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
prompt = f"{request.system}\n\n{request.prompt}" if request.system else request.prompt
|
|
107
|
+
inputs = self.tokenizer(prompt, return_tensors="pt")
|
|
108
|
+
outputs = self.model.generate(
|
|
109
|
+
**inputs,
|
|
110
|
+
max_new_tokens=request.max_tokens,
|
|
111
|
+
do_sample=request.temperature > 0,
|
|
112
|
+
temperature=max(request.temperature, 1e-5),
|
|
113
|
+
**dict(self.generation_kwargs),
|
|
114
|
+
)
|
|
115
|
+
sequences = outputs.sequences if hasattr(outputs, "sequences") else outputs
|
|
116
|
+
try:
|
|
117
|
+
input_shape = inputs["input_ids"].shape
|
|
118
|
+
if len(input_shape) != 2 or int(input_shape[0]) != 1:
|
|
119
|
+
raise ValueError("input_ids must contain exactly one two-dimensional sequence")
|
|
120
|
+
input_width = int(input_shape[1])
|
|
121
|
+
if input_width < 1:
|
|
122
|
+
raise ValueError("input_ids sequence must not be empty")
|
|
123
|
+
|
|
124
|
+
sequence_shape = sequences.shape
|
|
125
|
+
if len(sequence_shape) != 2 or int(sequence_shape[0]) != 1:
|
|
126
|
+
raise ValueError("generate must return exactly one two-dimensional sequence")
|
|
127
|
+
sequence_width = int(sequence_shape[1])
|
|
128
|
+
if sequence_width < input_width:
|
|
129
|
+
raise ValueError("generated sequence is shorter than its input")
|
|
130
|
+
|
|
131
|
+
generated_sequence = sequences[0]
|
|
132
|
+
new_tokens = generated_sequence[input_width:]
|
|
133
|
+
output_tokens = sequence_width - input_width
|
|
134
|
+
except (AttributeError, IndexError, KeyError, OverflowError, TypeError, ValueError) as exc:
|
|
135
|
+
raise ValidationError(
|
|
136
|
+
"Transformers model.generate returned an unsupported sequence shape"
|
|
137
|
+
) from exc
|
|
138
|
+
attention_mask = inputs.get("attention_mask")
|
|
139
|
+
if attention_mask is None:
|
|
140
|
+
input_tokens = input_width
|
|
141
|
+
else:
|
|
142
|
+
try:
|
|
143
|
+
mask_shape = attention_mask.shape
|
|
144
|
+
if (
|
|
145
|
+
len(mask_shape) != 2
|
|
146
|
+
or int(mask_shape[0]) != 1
|
|
147
|
+
or int(mask_shape[1]) != input_width
|
|
148
|
+
):
|
|
149
|
+
raise ValueError("attention_mask shape must match the single input sequence")
|
|
150
|
+
input_tokens = int(attention_mask.sum())
|
|
151
|
+
if input_tokens < 0 or input_tokens > input_width:
|
|
152
|
+
raise ValueError("attention_mask token count is outside the input width")
|
|
153
|
+
except (AttributeError, OverflowError, TypeError, ValueError) as exc:
|
|
154
|
+
raise ValidationError(
|
|
155
|
+
"Transformers tokenizer returned an unsupported attention_mask"
|
|
156
|
+
) from exc
|
|
157
|
+
text = self.tokenizer.decode(new_tokens, skip_special_tokens=True)
|
|
158
|
+
return GenerationResponse(
|
|
159
|
+
text=text,
|
|
160
|
+
model=self.model_id,
|
|
161
|
+
usage=Usage(input_tokens=input_tokens, output_tokens=output_tokens),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
165
|
+
return {
|
|
166
|
+
"backend": "transformers",
|
|
167
|
+
"model_id": self.model_id,
|
|
168
|
+
"model_revision": self.model_revision,
|
|
169
|
+
"tokenizer_revision": self.tokenizer_revision,
|
|
170
|
+
"generation_kwargs": dict(self.generation_kwargs),
|
|
171
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Retrieval backend contracts and small deterministic implementations."""
|
|
2
|
+
|
|
3
|
+
from .base import Document, RetrievalResult, Retriever
|
|
4
|
+
from .dense import (
|
|
5
|
+
DENSE_RETRIEVER_PROFILE_IDS,
|
|
6
|
+
RETRIEVER_PROFILES,
|
|
7
|
+
BatchTextEncoder,
|
|
8
|
+
DenseIndexSummary,
|
|
9
|
+
EmbeddingBackendEncoder,
|
|
10
|
+
RetrieverProfile,
|
|
11
|
+
SentenceTransformerDenseRetriever,
|
|
12
|
+
build_retriever,
|
|
13
|
+
retriever_profile,
|
|
14
|
+
)
|
|
15
|
+
from .memory import FunctionRetriever, InMemoryBM25Retriever, default_tokenize
|
|
16
|
+
from .sqlite_fts import SQLiteFTS5Retriever
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"DENSE_RETRIEVER_PROFILE_IDS",
|
|
20
|
+
"RETRIEVER_PROFILES",
|
|
21
|
+
"BatchTextEncoder",
|
|
22
|
+
"DenseIndexSummary",
|
|
23
|
+
"Document",
|
|
24
|
+
"EmbeddingBackendEncoder",
|
|
25
|
+
"FunctionRetriever",
|
|
26
|
+
"InMemoryBM25Retriever",
|
|
27
|
+
"RetrievalResult",
|
|
28
|
+
"Retriever",
|
|
29
|
+
"RetrieverProfile",
|
|
30
|
+
"SQLiteFTS5Retriever",
|
|
31
|
+
"SentenceTransformerDenseRetriever",
|
|
32
|
+
"build_retriever",
|
|
33
|
+
"default_tokenize",
|
|
34
|
+
"retriever_profile",
|
|
35
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Standard knowledge retrieval interfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Any, Protocol, runtime_checkable
|
|
8
|
+
|
|
9
|
+
from agentinputlab.contracts.errors import ValidationError
|
|
10
|
+
from agentinputlab.contracts.serialization import freeze_mapping
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True, slots=True)
|
|
14
|
+
class Document:
|
|
15
|
+
doc_id: str
|
|
16
|
+
text: str
|
|
17
|
+
title: str = ""
|
|
18
|
+
corpus_revision: str = ""
|
|
19
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
20
|
+
|
|
21
|
+
def __post_init__(self) -> None:
|
|
22
|
+
if not self.doc_id:
|
|
23
|
+
raise ValidationError("document requires a non-empty doc_id")
|
|
24
|
+
object.__setattr__(self, "metadata", freeze_mapping(self.metadata))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True, slots=True)
|
|
28
|
+
class RetrievalResult:
|
|
29
|
+
document: Document
|
|
30
|
+
score: float
|
|
31
|
+
rank: int
|
|
32
|
+
features: Mapping[str, float] = field(default_factory=dict)
|
|
33
|
+
|
|
34
|
+
def __post_init__(self) -> None:
|
|
35
|
+
if not isinstance(self.document, Document):
|
|
36
|
+
raise ValidationError("retrieval result requires a Document")
|
|
37
|
+
if self.rank < 1:
|
|
38
|
+
raise ValidationError("retrieval result rank must be positive")
|
|
39
|
+
object.__setattr__(self, "features", freeze_mapping(self.features))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@runtime_checkable
|
|
43
|
+
class Retriever(Protocol):
|
|
44
|
+
retriever_id: str
|
|
45
|
+
|
|
46
|
+
def retrieve(self, query: str, *, top_k: int = 5) -> tuple[RetrievalResult, ...]:
|
|
47
|
+
"""Return deterministic ranked candidates."""
|
|
48
|
+
|
|
49
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
50
|
+
"""Return corpus/index/retriever identity for balance auditing."""
|