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,695 @@
|
|
|
1
|
+
"""OpenAPI and Python-callable tool normalization with injected transport.
|
|
2
|
+
|
|
3
|
+
No HTTP implementation is bundled here. Calling an OpenAPI operation requires
|
|
4
|
+
an explicit transport object, which keeps imports, construction, schema
|
|
5
|
+
inspection, and fingerprinting offline by default.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import dataclasses
|
|
11
|
+
import inspect
|
|
12
|
+
import math
|
|
13
|
+
import re
|
|
14
|
+
import types
|
|
15
|
+
from collections import Counter
|
|
16
|
+
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
17
|
+
from collections.abc import Mapping as ABCMapping
|
|
18
|
+
from contextlib import suppress
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from enum import Enum
|
|
21
|
+
from typing import (
|
|
22
|
+
Any,
|
|
23
|
+
Literal,
|
|
24
|
+
Protocol,
|
|
25
|
+
Union,
|
|
26
|
+
get_args,
|
|
27
|
+
get_origin,
|
|
28
|
+
get_type_hints,
|
|
29
|
+
runtime_checkable,
|
|
30
|
+
)
|
|
31
|
+
from urllib.parse import quote, urlsplit
|
|
32
|
+
|
|
33
|
+
from agentinputlab.contracts.errors import ValidationError
|
|
34
|
+
from agentinputlab.contracts.serialization import (
|
|
35
|
+
canonical_json,
|
|
36
|
+
content_hash,
|
|
37
|
+
freeze_mapping,
|
|
38
|
+
to_jsonable,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
from .base import ToolCall, ToolConnector, ToolResult, ToolSpec
|
|
42
|
+
|
|
43
|
+
_HTTP_METHODS = frozenset({"get", "put", "post", "delete", "options", "head", "patch", "trace"})
|
|
44
|
+
_SAFE_NAME = re.compile(r"[^a-zA-Z0-9_.-]+")
|
|
45
|
+
_SENSITIVE_HEADERS = frozenset(
|
|
46
|
+
{
|
|
47
|
+
"authorization",
|
|
48
|
+
"connection",
|
|
49
|
+
"cookie",
|
|
50
|
+
"host",
|
|
51
|
+
"proxy-authorization",
|
|
52
|
+
"set-cookie",
|
|
53
|
+
"transfer-encoding",
|
|
54
|
+
"x-api-key",
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _json_pointer(document: Mapping[str, Any], ref: str) -> Any:
|
|
60
|
+
if not ref.startswith("#/"):
|
|
61
|
+
raise ValidationError(f"only local OpenAPI references are supported: {ref}")
|
|
62
|
+
value: Any = document
|
|
63
|
+
for raw_segment in ref[2:].split("/"):
|
|
64
|
+
segment = raw_segment.replace("~1", "/").replace("~0", "~")
|
|
65
|
+
if not isinstance(value, Mapping) or segment not in value:
|
|
66
|
+
raise ValidationError(f"unresolved OpenAPI reference: {ref}")
|
|
67
|
+
value = value[segment]
|
|
68
|
+
return value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _normalize_schema_node(
|
|
72
|
+
value: Any,
|
|
73
|
+
*,
|
|
74
|
+
document: Mapping[str, Any] | None,
|
|
75
|
+
resolving: frozenset[str],
|
|
76
|
+
) -> Any:
|
|
77
|
+
if isinstance(value, bool) or value is None:
|
|
78
|
+
return value
|
|
79
|
+
if isinstance(value, (list, tuple)):
|
|
80
|
+
return [
|
|
81
|
+
_normalize_schema_node(item, document=document, resolving=resolving) for item in value
|
|
82
|
+
]
|
|
83
|
+
if not isinstance(value, Mapping):
|
|
84
|
+
return to_jsonable(value)
|
|
85
|
+
|
|
86
|
+
if "$ref" in value:
|
|
87
|
+
ref = value["$ref"]
|
|
88
|
+
if not isinstance(ref, str):
|
|
89
|
+
raise ValidationError("JSON Schema $ref must be a string")
|
|
90
|
+
if document is None or not ref.startswith("#/") or ref in resolving:
|
|
91
|
+
return {"$ref": ref}
|
|
92
|
+
resolved = _json_pointer(document, ref)
|
|
93
|
+
normalized = _normalize_schema_node(
|
|
94
|
+
resolved,
|
|
95
|
+
document=document,
|
|
96
|
+
resolving=resolving | {ref},
|
|
97
|
+
)
|
|
98
|
+
siblings = {key: item for key, item in value.items() if key != "$ref"}
|
|
99
|
+
if not siblings:
|
|
100
|
+
return normalized
|
|
101
|
+
return {
|
|
102
|
+
"allOf": [
|
|
103
|
+
normalized,
|
|
104
|
+
_normalize_schema_node(siblings, document=document, resolving=resolving),
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
result: dict[str, Any] = {}
|
|
109
|
+
nullable = value.get("nullable") is True
|
|
110
|
+
for key in sorted(value):
|
|
111
|
+
if key in {"nullable", "readOnly", "writeOnly", "xml", "externalDocs"}:
|
|
112
|
+
continue
|
|
113
|
+
item = value[key]
|
|
114
|
+
if key == "properties":
|
|
115
|
+
if not isinstance(item, Mapping):
|
|
116
|
+
raise ValidationError("JSON Schema properties must be an object")
|
|
117
|
+
result[key] = {
|
|
118
|
+
name: _normalize_schema_node(
|
|
119
|
+
item[name],
|
|
120
|
+
document=document,
|
|
121
|
+
resolving=resolving,
|
|
122
|
+
)
|
|
123
|
+
for name in sorted(item)
|
|
124
|
+
}
|
|
125
|
+
elif key == "required":
|
|
126
|
+
if not isinstance(item, (list, tuple)) or not all(
|
|
127
|
+
isinstance(name, str) for name in item
|
|
128
|
+
):
|
|
129
|
+
raise ValidationError("JSON Schema required must be an array of strings")
|
|
130
|
+
result[key] = sorted(set(item))
|
|
131
|
+
elif key == "enum":
|
|
132
|
+
if not isinstance(item, (list, tuple)):
|
|
133
|
+
raise ValidationError("JSON Schema enum must be an array")
|
|
134
|
+
result[key] = sorted(
|
|
135
|
+
(to_jsonable(member) for member in item),
|
|
136
|
+
key=canonical_json,
|
|
137
|
+
)
|
|
138
|
+
else:
|
|
139
|
+
result[key] = _normalize_schema_node(
|
|
140
|
+
item,
|
|
141
|
+
document=document,
|
|
142
|
+
resolving=resolving,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if "properties" in result and "type" not in result:
|
|
146
|
+
result["type"] = "object"
|
|
147
|
+
if nullable:
|
|
148
|
+
existing = result.get("type")
|
|
149
|
+
if isinstance(existing, str):
|
|
150
|
+
result["type"] = [existing, "null"]
|
|
151
|
+
elif isinstance(existing, list):
|
|
152
|
+
result["type"] = sorted(set(existing) | {"null"})
|
|
153
|
+
elif "anyOf" in result:
|
|
154
|
+
result["anyOf"] = [*result["anyOf"], {"type": "null"}]
|
|
155
|
+
else:
|
|
156
|
+
result["anyOf"] = [{"type": "null"}, {}]
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def normalize_json_schema(
|
|
161
|
+
schema: Mapping[str, Any] | None,
|
|
162
|
+
*,
|
|
163
|
+
document: Mapping[str, Any] | None = None,
|
|
164
|
+
) -> Mapping[str, Any]:
|
|
165
|
+
"""Return a stable, local-ref-resolved JSON Schema mapping."""
|
|
166
|
+
|
|
167
|
+
if schema is None:
|
|
168
|
+
return {"type": "object", "properties": {}, "additionalProperties": False}
|
|
169
|
+
if not isinstance(schema, Mapping):
|
|
170
|
+
raise ValidationError("tool input schema must be an object")
|
|
171
|
+
normalized = _normalize_schema_node(schema, document=document, resolving=frozenset())
|
|
172
|
+
if not isinstance(normalized, Mapping):
|
|
173
|
+
raise ValidationError("normalized tool input schema must be an object")
|
|
174
|
+
if not normalized:
|
|
175
|
+
return {"type": "object", "properties": {}, "additionalProperties": False}
|
|
176
|
+
return normalized
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def validate_json_value(value: Any, schema: Mapping[str, Any], *, path: str = "value") -> None:
|
|
180
|
+
"""Validate the conservative JSON-Schema subset exposed by connectors."""
|
|
181
|
+
|
|
182
|
+
if "allOf" in schema:
|
|
183
|
+
variants = schema["allOf"]
|
|
184
|
+
if not isinstance(variants, (list, tuple)) or not all(
|
|
185
|
+
isinstance(variant, Mapping) for variant in variants
|
|
186
|
+
):
|
|
187
|
+
raise ValidationError(f"{path}: allOf must be an array of schemas")
|
|
188
|
+
for variant in variants:
|
|
189
|
+
validate_json_value(value, variant, path=path)
|
|
190
|
+
if "oneOf" in schema:
|
|
191
|
+
variants = schema["oneOf"]
|
|
192
|
+
if not isinstance(variants, (list, tuple)):
|
|
193
|
+
raise ValidationError(f"{path}: oneOf must be an array")
|
|
194
|
+
matches = 0
|
|
195
|
+
for variant in variants:
|
|
196
|
+
if not isinstance(variant, Mapping):
|
|
197
|
+
continue
|
|
198
|
+
try:
|
|
199
|
+
validate_json_value(value, variant, path=path)
|
|
200
|
+
matches += 1
|
|
201
|
+
except ValidationError:
|
|
202
|
+
continue
|
|
203
|
+
if matches != 1:
|
|
204
|
+
raise ValidationError(f"{path}: value must match exactly one allowed schema")
|
|
205
|
+
if "anyOf" in schema:
|
|
206
|
+
variants = schema["anyOf"]
|
|
207
|
+
if not isinstance(variants, (list, tuple)):
|
|
208
|
+
raise ValidationError(f"{path}: anyOf must be an array")
|
|
209
|
+
for variant in variants:
|
|
210
|
+
if not isinstance(variant, Mapping):
|
|
211
|
+
continue
|
|
212
|
+
try:
|
|
213
|
+
validate_json_value(value, variant, path=path)
|
|
214
|
+
break
|
|
215
|
+
except ValidationError:
|
|
216
|
+
continue
|
|
217
|
+
else:
|
|
218
|
+
raise ValidationError(f"{path}: value does not match any allowed schema")
|
|
219
|
+
if "enum" in schema and value not in schema["enum"]:
|
|
220
|
+
raise ValidationError(f"{path}: value is not in the allowed enum")
|
|
221
|
+
expected = schema.get("type")
|
|
222
|
+
expected_types = {expected} if isinstance(expected, str) else set(expected or ())
|
|
223
|
+
if expected_types and not _matches_json_type(value, expected_types):
|
|
224
|
+
raise ValidationError(f"{path}: expected JSON type {'|'.join(sorted(expected_types))}")
|
|
225
|
+
if isinstance(value, Mapping):
|
|
226
|
+
properties = schema.get("properties", {})
|
|
227
|
+
if isinstance(properties, Mapping):
|
|
228
|
+
required = set(schema.get("required", ()))
|
|
229
|
+
missing = sorted(required - set(value))
|
|
230
|
+
if missing:
|
|
231
|
+
raise ValidationError(f"{path}: missing required keys {', '.join(missing)}")
|
|
232
|
+
if schema.get("additionalProperties") is False:
|
|
233
|
+
unknown = sorted(set(value) - set(properties))
|
|
234
|
+
if unknown:
|
|
235
|
+
raise ValidationError(f"{path}: unknown keys {', '.join(unknown)}")
|
|
236
|
+
for key, item in value.items():
|
|
237
|
+
child = properties.get(key)
|
|
238
|
+
if isinstance(child, Mapping):
|
|
239
|
+
validate_json_value(item, child, path=f"{path}.{key}")
|
|
240
|
+
elif key not in properties and isinstance(
|
|
241
|
+
schema.get("additionalProperties"), Mapping
|
|
242
|
+
):
|
|
243
|
+
validate_json_value(
|
|
244
|
+
item,
|
|
245
|
+
schema["additionalProperties"],
|
|
246
|
+
path=f"{path}.{key}",
|
|
247
|
+
)
|
|
248
|
+
if isinstance(value, (list, tuple)):
|
|
249
|
+
item_schema = schema.get("items")
|
|
250
|
+
if isinstance(item_schema, Mapping):
|
|
251
|
+
for index, item in enumerate(value):
|
|
252
|
+
validate_json_value(item, item_schema, path=f"{path}[{index}]")
|
|
253
|
+
if "minItems" in schema and len(value) < int(schema["minItems"]):
|
|
254
|
+
raise ValidationError(f"{path}: array is shorter than minItems")
|
|
255
|
+
if "maxItems" in schema and len(value) > int(schema["maxItems"]):
|
|
256
|
+
raise ValidationError(f"{path}: array is longer than maxItems")
|
|
257
|
+
if isinstance(value, str):
|
|
258
|
+
if "minLength" in schema and len(value) < int(schema["minLength"]):
|
|
259
|
+
raise ValidationError(f"{path}: string is shorter than minLength")
|
|
260
|
+
if "maxLength" in schema and len(value) > int(schema["maxLength"]):
|
|
261
|
+
raise ValidationError(f"{path}: string is longer than maxLength")
|
|
262
|
+
if "pattern" in schema and re.search(str(schema["pattern"]), value) is None:
|
|
263
|
+
raise ValidationError(f"{path}: string does not match pattern")
|
|
264
|
+
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
265
|
+
if not math.isfinite(float(value)):
|
|
266
|
+
raise ValidationError(f"{path}: number must be finite")
|
|
267
|
+
if "minimum" in schema and value < schema["minimum"]:
|
|
268
|
+
raise ValidationError(f"{path}: number is below minimum")
|
|
269
|
+
if "maximum" in schema and value > schema["maximum"]:
|
|
270
|
+
raise ValidationError(f"{path}: number is above maximum")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _matches_json_type(value: Any, expected: set[str]) -> bool:
|
|
274
|
+
return any(
|
|
275
|
+
(
|
|
276
|
+
(kind == "null" and value is None)
|
|
277
|
+
or (kind == "boolean" and isinstance(value, bool))
|
|
278
|
+
or (kind == "integer" and isinstance(value, int) and not isinstance(value, bool))
|
|
279
|
+
or (
|
|
280
|
+
kind == "number" and isinstance(value, (int, float)) and not isinstance(value, bool)
|
|
281
|
+
)
|
|
282
|
+
or (kind == "string" and isinstance(value, str))
|
|
283
|
+
or (kind == "array" and isinstance(value, (list, tuple)))
|
|
284
|
+
or (kind == "object" and isinstance(value, Mapping))
|
|
285
|
+
)
|
|
286
|
+
for kind in expected
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _annotation_schema(annotation: Any) -> Mapping[str, Any]:
|
|
291
|
+
if annotation in {Any, inspect.Signature.empty}:
|
|
292
|
+
return {}
|
|
293
|
+
if annotation is None or annotation is type(None):
|
|
294
|
+
return {"type": "null"}
|
|
295
|
+
if annotation is str:
|
|
296
|
+
return {"type": "string"}
|
|
297
|
+
if annotation is bool:
|
|
298
|
+
return {"type": "boolean"}
|
|
299
|
+
if annotation is int:
|
|
300
|
+
return {"type": "integer"}
|
|
301
|
+
if annotation is float:
|
|
302
|
+
return {"type": "number"}
|
|
303
|
+
|
|
304
|
+
origin = get_origin(annotation)
|
|
305
|
+
arguments = get_args(annotation)
|
|
306
|
+
if origin is Literal:
|
|
307
|
+
values = [to_jsonable(item) for item in arguments]
|
|
308
|
+
return {"enum": sorted(values, key=canonical_json)}
|
|
309
|
+
if origin in {Union, types.UnionType}:
|
|
310
|
+
variants = [_annotation_schema(item) for item in arguments]
|
|
311
|
+
return {"anyOf": variants}
|
|
312
|
+
if origin in {list, tuple, set, frozenset, Sequence, Iterable}:
|
|
313
|
+
item_schema = _annotation_schema(arguments[0]) if arguments else {}
|
|
314
|
+
return {"type": "array", "items": item_schema}
|
|
315
|
+
if origin in {dict, Mapping, ABCMapping}:
|
|
316
|
+
value_schema = _annotation_schema(arguments[1]) if len(arguments) > 1 else {}
|
|
317
|
+
return {"type": "object", "additionalProperties": value_schema}
|
|
318
|
+
if inspect.isclass(annotation) and issubclass(annotation, Enum):
|
|
319
|
+
values = [to_jsonable(item.value) for item in annotation]
|
|
320
|
+
return {"enum": sorted(values, key=canonical_json)}
|
|
321
|
+
if inspect.isclass(annotation) and dataclasses.is_dataclass(annotation):
|
|
322
|
+
properties: dict[str, Any] = {}
|
|
323
|
+
required: list[str] = []
|
|
324
|
+
for item in dataclasses.fields(annotation):
|
|
325
|
+
properties[item.name] = _annotation_schema(item.type)
|
|
326
|
+
if item.default is dataclasses.MISSING and item.default_factory is dataclasses.MISSING:
|
|
327
|
+
required.append(item.name)
|
|
328
|
+
result: dict[str, Any] = {
|
|
329
|
+
"type": "object",
|
|
330
|
+
"properties": properties,
|
|
331
|
+
"additionalProperties": False,
|
|
332
|
+
}
|
|
333
|
+
if required:
|
|
334
|
+
result["required"] = sorted(required)
|
|
335
|
+
return result
|
|
336
|
+
return {"type": "string", "x-python-type": getattr(annotation, "__name__", str(annotation))}
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def python_tool_spec(
|
|
340
|
+
function: Callable[..., Any],
|
|
341
|
+
*,
|
|
342
|
+
name: str | None = None,
|
|
343
|
+
description: str | None = None,
|
|
344
|
+
read_only: bool = False,
|
|
345
|
+
network: bool = False,
|
|
346
|
+
) -> ToolSpec:
|
|
347
|
+
"""Derive a deterministic tool schema from a typed Python callable."""
|
|
348
|
+
|
|
349
|
+
signature = inspect.signature(function)
|
|
350
|
+
try:
|
|
351
|
+
hints = get_type_hints(function)
|
|
352
|
+
except (NameError, TypeError):
|
|
353
|
+
hints = dict(getattr(function, "__annotations__", {}))
|
|
354
|
+
|
|
355
|
+
properties: dict[str, Any] = {}
|
|
356
|
+
required: list[str] = []
|
|
357
|
+
for parameter_name, parameter in signature.parameters.items():
|
|
358
|
+
if parameter.kind in {
|
|
359
|
+
inspect.Parameter.VAR_POSITIONAL,
|
|
360
|
+
inspect.Parameter.VAR_KEYWORD,
|
|
361
|
+
}:
|
|
362
|
+
raise ValidationError("variadic Python tools require an explicit ToolSpec")
|
|
363
|
+
schema = dict(_annotation_schema(hints.get(parameter_name, parameter.annotation)))
|
|
364
|
+
if parameter.default is inspect.Signature.empty:
|
|
365
|
+
required.append(parameter_name)
|
|
366
|
+
else:
|
|
367
|
+
with suppress(TypeError):
|
|
368
|
+
schema["default"] = to_jsonable(parameter.default)
|
|
369
|
+
properties[parameter_name] = schema
|
|
370
|
+
input_schema: dict[str, Any] = {
|
|
371
|
+
"type": "object",
|
|
372
|
+
"properties": properties,
|
|
373
|
+
"additionalProperties": False,
|
|
374
|
+
}
|
|
375
|
+
if required:
|
|
376
|
+
input_schema["required"] = sorted(required)
|
|
377
|
+
tool_name = name or function.__name__
|
|
378
|
+
if not tool_name or not isinstance(tool_name, str):
|
|
379
|
+
raise ValidationError("Python tool name must be a non-empty string")
|
|
380
|
+
return ToolSpec(
|
|
381
|
+
name=tool_name,
|
|
382
|
+
description=description if description is not None else (inspect.getdoc(function) or ""),
|
|
383
|
+
input_schema=normalize_json_schema(input_schema),
|
|
384
|
+
read_only=read_only,
|
|
385
|
+
network=network,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@runtime_checkable
|
|
390
|
+
class OpenAPITransport(Protocol):
|
|
391
|
+
"""Injected request executor; implementations may be HTTP, mock, or replay."""
|
|
392
|
+
|
|
393
|
+
def request(
|
|
394
|
+
self,
|
|
395
|
+
method: str,
|
|
396
|
+
path: str,
|
|
397
|
+
*,
|
|
398
|
+
query: Mapping[str, Any],
|
|
399
|
+
headers: Mapping[str, str],
|
|
400
|
+
body: Any,
|
|
401
|
+
) -> Any: ...
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
@dataclass(frozen=True, slots=True)
|
|
405
|
+
class _Operation:
|
|
406
|
+
tool: ToolSpec
|
|
407
|
+
method: str
|
|
408
|
+
path: str
|
|
409
|
+
argument_locations: Mapping[str, tuple[str, str]]
|
|
410
|
+
body_argument: str | None
|
|
411
|
+
|
|
412
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
413
|
+
return {
|
|
414
|
+
"tool": self.tool,
|
|
415
|
+
"method": self.method,
|
|
416
|
+
"path": self.path,
|
|
417
|
+
"argument_locations": self.argument_locations,
|
|
418
|
+
"body_argument": self.body_argument,
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _resolve_object(value: Any, document: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
423
|
+
if not isinstance(value, Mapping):
|
|
424
|
+
raise ValidationError("OpenAPI component must be an object")
|
|
425
|
+
if "$ref" in value:
|
|
426
|
+
ref = value["$ref"]
|
|
427
|
+
if not isinstance(ref, str):
|
|
428
|
+
raise ValidationError("OpenAPI $ref must be a string")
|
|
429
|
+
resolved = _json_pointer(document, ref)
|
|
430
|
+
if not isinstance(resolved, Mapping):
|
|
431
|
+
raise ValidationError(f"OpenAPI reference does not resolve to an object: {ref}")
|
|
432
|
+
return resolved
|
|
433
|
+
return value
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _operation_name(method: str, path: str, operation: Mapping[str, Any]) -> str:
|
|
437
|
+
operation_id = operation.get("operationId")
|
|
438
|
+
if isinstance(operation_id, str) and operation_id.strip():
|
|
439
|
+
return _SAFE_NAME.sub("_", operation_id.strip()).strip("_")
|
|
440
|
+
stem = _SAFE_NAME.sub("_", path.strip("/")).strip("_") or "root"
|
|
441
|
+
return f"{method.lower()}_{stem}"
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _operation_description(operation: Mapping[str, Any]) -> str:
|
|
445
|
+
summary = operation.get("summary")
|
|
446
|
+
description = operation.get("description")
|
|
447
|
+
parts = [
|
|
448
|
+
item.strip() for item in (summary, description) if isinstance(item, str) and item.strip()
|
|
449
|
+
]
|
|
450
|
+
return "\n\n".join(parts)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
@dataclass(slots=True)
|
|
454
|
+
class OpenAPIToolConnector(ToolConnector):
|
|
455
|
+
"""Expose selected OpenAPI operations through the common tool contract."""
|
|
456
|
+
|
|
457
|
+
document: Mapping[str, Any]
|
|
458
|
+
transport: OpenAPITransport
|
|
459
|
+
connector_id: str = "openapi"
|
|
460
|
+
base_url_identity: str = ""
|
|
461
|
+
allow_operations: tuple[str, ...] = ()
|
|
462
|
+
allow_mutating: bool = False
|
|
463
|
+
allow_network_calls: bool = False
|
|
464
|
+
network: bool = True
|
|
465
|
+
_operations: Mapping[str, _Operation] = field(init=False, repr=False)
|
|
466
|
+
|
|
467
|
+
def __post_init__(self) -> None:
|
|
468
|
+
self.allow_operations = tuple(self.allow_operations)
|
|
469
|
+
self._operations = self._build_operations()
|
|
470
|
+
self.document = freeze_mapping(self.document)
|
|
471
|
+
|
|
472
|
+
def _build_operations(self) -> Mapping[str, _Operation]:
|
|
473
|
+
paths = self.document.get("paths", {})
|
|
474
|
+
if not isinstance(paths, Mapping):
|
|
475
|
+
raise ValidationError("OpenAPI document.paths must be an object")
|
|
476
|
+
operations: dict[str, _Operation] = {}
|
|
477
|
+
for path in sorted(paths):
|
|
478
|
+
if (
|
|
479
|
+
not isinstance(path, str)
|
|
480
|
+
or not path.startswith("/")
|
|
481
|
+
or path.startswith("//")
|
|
482
|
+
or "\\" in path
|
|
483
|
+
or urlsplit(path).scheme
|
|
484
|
+
or urlsplit(path).netloc
|
|
485
|
+
or urlsplit(path).query
|
|
486
|
+
or urlsplit(path).fragment
|
|
487
|
+
):
|
|
488
|
+
raise ValidationError(f"OpenAPI paths must be root-relative: {path!r}")
|
|
489
|
+
path_item = _resolve_object(paths[path], self.document)
|
|
490
|
+
inherited = path_item.get("parameters", [])
|
|
491
|
+
if not isinstance(inherited, list):
|
|
492
|
+
raise ValidationError(f"OpenAPI path parameters must be an array: {path}")
|
|
493
|
+
for method in sorted(key for key in path_item if key.lower() in _HTTP_METHODS):
|
|
494
|
+
raw_operation = _resolve_object(path_item[method], self.document)
|
|
495
|
+
tool_name = _operation_name(method, path, raw_operation)
|
|
496
|
+
if "*" not in self.allow_operations and tool_name not in self.allow_operations:
|
|
497
|
+
continue
|
|
498
|
+
if tool_name in operations:
|
|
499
|
+
raise ValidationError(
|
|
500
|
+
f"duplicate normalized OpenAPI operation name: {tool_name}"
|
|
501
|
+
)
|
|
502
|
+
own_parameters = raw_operation.get("parameters", [])
|
|
503
|
+
if not isinstance(own_parameters, list):
|
|
504
|
+
raise ValidationError(
|
|
505
|
+
f"OpenAPI operation parameters must be an array: {tool_name}"
|
|
506
|
+
)
|
|
507
|
+
# OpenAPI operation parameters override path-level parameters
|
|
508
|
+
# with the same ``(name, in)`` identity.
|
|
509
|
+
merged_parameters: dict[tuple[Any, Any], Mapping[str, Any]] = {}
|
|
510
|
+
for item in [*inherited, *own_parameters]:
|
|
511
|
+
parameter = _resolve_object(item, self.document)
|
|
512
|
+
merged_parameters[(parameter.get("name"), parameter.get("in"))] = parameter
|
|
513
|
+
parameters = list(merged_parameters.values())
|
|
514
|
+
name_counts = Counter(
|
|
515
|
+
item.get("name") for item in parameters if isinstance(item.get("name"), str)
|
|
516
|
+
)
|
|
517
|
+
properties: dict[str, Any] = {}
|
|
518
|
+
required: list[str] = []
|
|
519
|
+
locations: dict[str, tuple[str, str]] = {}
|
|
520
|
+
for parameter in parameters:
|
|
521
|
+
original_name = parameter.get("name")
|
|
522
|
+
location = parameter.get("in")
|
|
523
|
+
if not isinstance(original_name, str) or location not in {
|
|
524
|
+
"path",
|
|
525
|
+
"query",
|
|
526
|
+
"header",
|
|
527
|
+
"cookie",
|
|
528
|
+
}:
|
|
529
|
+
raise ValidationError(f"invalid OpenAPI parameter in operation {tool_name}")
|
|
530
|
+
if location == "cookie" or (
|
|
531
|
+
location == "header" and original_name.lower() in _SENSITIVE_HEADERS
|
|
532
|
+
):
|
|
533
|
+
raise ValidationError(
|
|
534
|
+
f"sensitive OpenAPI parameter is not model-exposed: {original_name}"
|
|
535
|
+
)
|
|
536
|
+
argument_name = (
|
|
537
|
+
original_name
|
|
538
|
+
if name_counts[original_name] == 1
|
|
539
|
+
else f"{location}__{original_name}"
|
|
540
|
+
)
|
|
541
|
+
properties[argument_name] = normalize_json_schema(
|
|
542
|
+
parameter.get("schema", {}),
|
|
543
|
+
document=self.document,
|
|
544
|
+
)
|
|
545
|
+
locations[argument_name] = (location, original_name)
|
|
546
|
+
if parameter.get("required") is True or location == "path":
|
|
547
|
+
required.append(argument_name)
|
|
548
|
+
|
|
549
|
+
body_argument: str | None = None
|
|
550
|
+
request_body = raw_operation.get("requestBody")
|
|
551
|
+
if request_body is not None:
|
|
552
|
+
request_body = _resolve_object(request_body, self.document)
|
|
553
|
+
content = request_body.get("content", {})
|
|
554
|
+
if not isinstance(content, Mapping) or not content:
|
|
555
|
+
raise ValidationError(f"requestBody has no content schemas: {tool_name}")
|
|
556
|
+
media_type = (
|
|
557
|
+
"application/json" if "application/json" in content else sorted(content)[0]
|
|
558
|
+
)
|
|
559
|
+
media = _resolve_object(content[media_type], self.document)
|
|
560
|
+
body_argument = "body" if "body" not in properties else "request_body"
|
|
561
|
+
properties[body_argument] = normalize_json_schema(
|
|
562
|
+
media.get("schema", {}),
|
|
563
|
+
document=self.document,
|
|
564
|
+
)
|
|
565
|
+
if request_body.get("required") is True:
|
|
566
|
+
required.append(body_argument)
|
|
567
|
+
|
|
568
|
+
input_schema: dict[str, Any] = {
|
|
569
|
+
"type": "object",
|
|
570
|
+
"properties": properties,
|
|
571
|
+
"additionalProperties": False,
|
|
572
|
+
}
|
|
573
|
+
if required:
|
|
574
|
+
input_schema["required"] = sorted(set(required))
|
|
575
|
+
tool = ToolSpec(
|
|
576
|
+
name=tool_name,
|
|
577
|
+
description=_operation_description(raw_operation),
|
|
578
|
+
input_schema=normalize_json_schema(input_schema),
|
|
579
|
+
read_only=method.lower() in {"get", "head", "options"},
|
|
580
|
+
network=self.network,
|
|
581
|
+
)
|
|
582
|
+
operations[tool_name] = _Operation(
|
|
583
|
+
tool=tool,
|
|
584
|
+
method=method.upper(),
|
|
585
|
+
path=path,
|
|
586
|
+
argument_locations=locations,
|
|
587
|
+
body_argument=body_argument,
|
|
588
|
+
)
|
|
589
|
+
return operations
|
|
590
|
+
|
|
591
|
+
def list_tools(self) -> tuple[ToolSpec, ...]:
|
|
592
|
+
return tuple(self._operations[name].tool for name in sorted(self._operations))
|
|
593
|
+
|
|
594
|
+
def call(self, request: ToolCall) -> ToolResult:
|
|
595
|
+
operation = self._operations.get(request.name)
|
|
596
|
+
if operation is None:
|
|
597
|
+
return ToolResult(call_id=request.call_id, error=f"unknown tool: {request.name}")
|
|
598
|
+
if not operation.tool.read_only and not self.allow_mutating:
|
|
599
|
+
return ToolResult(
|
|
600
|
+
call_id=request.call_id,
|
|
601
|
+
error=f"mutating tool is disabled by policy: {request.name}",
|
|
602
|
+
)
|
|
603
|
+
if operation.tool.network and not self.allow_network_calls:
|
|
604
|
+
return ToolResult(
|
|
605
|
+
call_id=request.call_id,
|
|
606
|
+
error=f"network tool is disabled by policy: {request.name}",
|
|
607
|
+
)
|
|
608
|
+
arguments = dict(request.arguments)
|
|
609
|
+
properties = operation.tool.input_schema.get("properties", {})
|
|
610
|
+
required = operation.tool.input_schema.get("required", [])
|
|
611
|
+
unknown = sorted(set(arguments) - set(properties))
|
|
612
|
+
missing = sorted(set(required) - set(arguments))
|
|
613
|
+
if unknown:
|
|
614
|
+
return ToolResult(
|
|
615
|
+
call_id=request.call_id,
|
|
616
|
+
error=f"unknown arguments for {request.name}: {', '.join(unknown)}",
|
|
617
|
+
)
|
|
618
|
+
if missing:
|
|
619
|
+
return ToolResult(
|
|
620
|
+
call_id=request.call_id,
|
|
621
|
+
error=f"missing required arguments for {request.name}: {', '.join(missing)}",
|
|
622
|
+
)
|
|
623
|
+
try:
|
|
624
|
+
for name, value in arguments.items():
|
|
625
|
+
schema = properties.get(name, {})
|
|
626
|
+
if isinstance(schema, Mapping):
|
|
627
|
+
validate_json_value(value, schema, path=name)
|
|
628
|
+
except ValidationError as exc:
|
|
629
|
+
return ToolResult(call_id=request.call_id, error=str(exc))
|
|
630
|
+
|
|
631
|
+
path = operation.path
|
|
632
|
+
query: dict[str, Any] = {}
|
|
633
|
+
headers: dict[str, str] = {}
|
|
634
|
+
cookies: dict[str, str] = {}
|
|
635
|
+
for argument_name, (location, original_name) in operation.argument_locations.items():
|
|
636
|
+
if argument_name not in arguments:
|
|
637
|
+
continue
|
|
638
|
+
value = arguments[argument_name]
|
|
639
|
+
if location == "path":
|
|
640
|
+
path = path.replace("{" + original_name + "}", quote(str(value), safe=""))
|
|
641
|
+
elif location == "query":
|
|
642
|
+
query[original_name] = value
|
|
643
|
+
elif location == "header":
|
|
644
|
+
headers[original_name] = str(value)
|
|
645
|
+
elif location == "cookie":
|
|
646
|
+
cookies[original_name] = str(value)
|
|
647
|
+
if cookies:
|
|
648
|
+
headers["Cookie"] = "; ".join(f"{name}={cookies[name]}" for name in sorted(cookies))
|
|
649
|
+
body = arguments.get(operation.body_argument) if operation.body_argument else None
|
|
650
|
+
try:
|
|
651
|
+
output = self.transport.request(
|
|
652
|
+
operation.method,
|
|
653
|
+
path,
|
|
654
|
+
query=query,
|
|
655
|
+
headers=headers,
|
|
656
|
+
body=body,
|
|
657
|
+
)
|
|
658
|
+
return ToolResult(
|
|
659
|
+
call_id=request.call_id,
|
|
660
|
+
output=output,
|
|
661
|
+
metadata={"connector": "openapi", "operation": request.name},
|
|
662
|
+
)
|
|
663
|
+
except Exception as exc:
|
|
664
|
+
return ToolResult(
|
|
665
|
+
call_id=request.call_id,
|
|
666
|
+
error=f"{type(exc).__name__}: {exc}",
|
|
667
|
+
metadata={"connector": "openapi", "operation": request.name},
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
671
|
+
operation_fingerprints = [
|
|
672
|
+
self._operations[name].fingerprint() for name in sorted(self._operations)
|
|
673
|
+
]
|
|
674
|
+
return {
|
|
675
|
+
"connector_id": self.connector_id,
|
|
676
|
+
"protocol": "openapi",
|
|
677
|
+
"implementation": "agentinputlab.openapi.v1",
|
|
678
|
+
"document_hash": content_hash(self.document),
|
|
679
|
+
"operation_schema_hash": content_hash(operation_fingerprints),
|
|
680
|
+
"operation_count": len(operation_fingerprints),
|
|
681
|
+
"base_url_identity_hash": content_hash(self.base_url_identity),
|
|
682
|
+
"network": self.network,
|
|
683
|
+
"allow_operations": sorted(self.allow_operations),
|
|
684
|
+
"allow_mutating": self.allow_mutating,
|
|
685
|
+
"allow_network_calls": self.allow_network_calls,
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
__all__ = [
|
|
690
|
+
"OpenAPIToolConnector",
|
|
691
|
+
"OpenAPITransport",
|
|
692
|
+
"normalize_json_schema",
|
|
693
|
+
"python_tool_spec",
|
|
694
|
+
"validate_json_value",
|
|
695
|
+
]
|