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,613 @@
|
|
|
1
|
+
"""Dependency-free HTTP server for the packaged local AgentInputLab Studio."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import mimetypes
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import socket
|
|
11
|
+
import sys
|
|
12
|
+
from http import HTTPStatus
|
|
13
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
from urllib.parse import urlsplit
|
|
17
|
+
|
|
18
|
+
from agentinputlab.contracts.errors import ArtifactError, ValidationError
|
|
19
|
+
|
|
20
|
+
from .campaigns import (
|
|
21
|
+
CampaignManager,
|
|
22
|
+
CampaignQueueFullError,
|
|
23
|
+
CampaignSecurityError,
|
|
24
|
+
CampaignValidationError,
|
|
25
|
+
disabled_campaign_catalog_payload,
|
|
26
|
+
)
|
|
27
|
+
from .engine import (
|
|
28
|
+
RunManager,
|
|
29
|
+
StudioQueueFullError,
|
|
30
|
+
StudioSecurityError,
|
|
31
|
+
StudioValidationError,
|
|
32
|
+
validate_config,
|
|
33
|
+
)
|
|
34
|
+
from .model_backends import (
|
|
35
|
+
STUDIO_MODEL_BACKEND_CONFIG_ENV,
|
|
36
|
+
STUDIO_MODEL_BACKEND_CONFIG_SHA256_ENV,
|
|
37
|
+
StudioModelBackendGate,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
MAX_REQUEST_BYTES = 65_536
|
|
41
|
+
LOOPBACK_HOSTS = frozenset({"127.0.0.1", "localhost", "::1"})
|
|
42
|
+
RUN_ID = re.compile(r"^[0-9a-f]{32}$")
|
|
43
|
+
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
|
44
|
+
STATIC_ROUTES = {
|
|
45
|
+
"/": "index.html",
|
|
46
|
+
"/index.html": "index.html",
|
|
47
|
+
"/result": "result.html",
|
|
48
|
+
"/result.html": "result.html",
|
|
49
|
+
"/campaigns": "campaigns.html",
|
|
50
|
+
"/campaigns.html": "campaigns.html",
|
|
51
|
+
"/styles.css": "styles.css",
|
|
52
|
+
"/app.js": "app.js",
|
|
53
|
+
"/result.js": "result.js",
|
|
54
|
+
"/campaigns.js": "campaigns.js",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class StudioHTTPServer(ThreadingHTTPServer):
|
|
59
|
+
daemon_threads = True
|
|
60
|
+
allow_reuse_address = True
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
server_address: tuple[str, int],
|
|
65
|
+
handler: type[BaseHTTPRequestHandler],
|
|
66
|
+
*,
|
|
67
|
+
manager: RunManager,
|
|
68
|
+
campaign_manager: CampaignManager | None = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
self.manager = manager
|
|
71
|
+
self.catalog = manager.catalog
|
|
72
|
+
self.campaign_manager = campaign_manager
|
|
73
|
+
self.address_family = socket.AF_INET6 if ":" in server_address[0] else socket.AF_INET
|
|
74
|
+
super().__init__(server_address, handler)
|
|
75
|
+
|
|
76
|
+
def server_close(self) -> None:
|
|
77
|
+
try:
|
|
78
|
+
super().server_close()
|
|
79
|
+
finally:
|
|
80
|
+
self.manager.shutdown(wait=True)
|
|
81
|
+
if self.campaign_manager is not None:
|
|
82
|
+
self.campaign_manager.shutdown(wait=True)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class StudioHandler(BaseHTTPRequestHandler):
|
|
86
|
+
server: StudioHTTPServer
|
|
87
|
+
protocol_version = "HTTP/1.1"
|
|
88
|
+
|
|
89
|
+
def _accept_host(self) -> bool:
|
|
90
|
+
raw_host = self.headers.get("Host", "")
|
|
91
|
+
try:
|
|
92
|
+
parsed = urlsplit(f"//{raw_host}")
|
|
93
|
+
_ = parsed.port
|
|
94
|
+
except ValueError:
|
|
95
|
+
parsed = None
|
|
96
|
+
if (
|
|
97
|
+
parsed is None
|
|
98
|
+
or parsed.hostname not in LOOPBACK_HOSTS
|
|
99
|
+
or parsed.username is not None
|
|
100
|
+
or parsed.password is not None
|
|
101
|
+
):
|
|
102
|
+
self._send_error(
|
|
103
|
+
HTTPStatus.MISDIRECTED_REQUEST,
|
|
104
|
+
"host_rejected",
|
|
105
|
+
"Host must be localhost, 127.0.0.1, or [::1]",
|
|
106
|
+
)
|
|
107
|
+
return False
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
def _security_headers(self) -> None:
|
|
111
|
+
self.send_header("X-Content-Type-Options", "nosniff")
|
|
112
|
+
self.send_header("X-Frame-Options", "DENY")
|
|
113
|
+
self.send_header("Referrer-Policy", "no-referrer")
|
|
114
|
+
self.send_header(
|
|
115
|
+
"Content-Security-Policy",
|
|
116
|
+
"default-src 'self'; script-src 'self'; style-src 'self'; "
|
|
117
|
+
"img-src 'self' data:; connect-src 'self'; object-src 'none'; "
|
|
118
|
+
"base-uri 'none'; frame-ancestors 'none'; form-action 'self'",
|
|
119
|
+
)
|
|
120
|
+
self.send_header(
|
|
121
|
+
"Permissions-Policy",
|
|
122
|
+
"camera=(), microphone=(), geolocation=(), payment=(), usb=()",
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def _send_json(
|
|
126
|
+
self,
|
|
127
|
+
payload: Any,
|
|
128
|
+
*,
|
|
129
|
+
status: HTTPStatus = HTTPStatus.OK,
|
|
130
|
+
) -> None:
|
|
131
|
+
data = json.dumps(
|
|
132
|
+
payload,
|
|
133
|
+
ensure_ascii=False,
|
|
134
|
+
sort_keys=True,
|
|
135
|
+
separators=(",", ":"),
|
|
136
|
+
allow_nan=False,
|
|
137
|
+
).encode("utf-8")
|
|
138
|
+
self.send_response(status)
|
|
139
|
+
self.send_header("Content-Type", "application/json; charset=utf-8")
|
|
140
|
+
self.send_header("Content-Length", str(len(data)))
|
|
141
|
+
self.send_header("Cache-Control", "no-store")
|
|
142
|
+
self._security_headers()
|
|
143
|
+
self.end_headers()
|
|
144
|
+
self.wfile.write(data)
|
|
145
|
+
|
|
146
|
+
def _send_error(self, status: HTTPStatus, code: str, message: str) -> None:
|
|
147
|
+
self._send_json(
|
|
148
|
+
{"error": {"code": code, "message": message}},
|
|
149
|
+
status=status,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def _read_json(self) -> Any:
|
|
153
|
+
content_type = self.headers.get("Content-Type", "").partition(";")[0].strip().lower()
|
|
154
|
+
if content_type != "application/json":
|
|
155
|
+
raise _HTTPInputError(
|
|
156
|
+
HTTPStatus.UNSUPPORTED_MEDIA_TYPE,
|
|
157
|
+
"content_type",
|
|
158
|
+
"Content-Type must be application/json",
|
|
159
|
+
)
|
|
160
|
+
raw_length = self.headers.get("Content-Length")
|
|
161
|
+
try:
|
|
162
|
+
length = int(raw_length or "0")
|
|
163
|
+
except ValueError as exc:
|
|
164
|
+
raise _HTTPInputError(
|
|
165
|
+
HTTPStatus.BAD_REQUEST,
|
|
166
|
+
"content_length",
|
|
167
|
+
"invalid Content-Length",
|
|
168
|
+
) from exc
|
|
169
|
+
if length < 1:
|
|
170
|
+
raise _HTTPInputError(HTTPStatus.BAD_REQUEST, "empty_body", "JSON body is required")
|
|
171
|
+
if length > MAX_REQUEST_BYTES:
|
|
172
|
+
raise _HTTPInputError(
|
|
173
|
+
HTTPStatus.REQUEST_ENTITY_TOO_LARGE,
|
|
174
|
+
"body_too_large",
|
|
175
|
+
f"request exceeds {MAX_REQUEST_BYTES} bytes",
|
|
176
|
+
)
|
|
177
|
+
data = self.rfile.read(length)
|
|
178
|
+
try:
|
|
179
|
+
return json.loads(data.decode("utf-8"))
|
|
180
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
181
|
+
raise _HTTPInputError(
|
|
182
|
+
HTTPStatus.BAD_REQUEST,
|
|
183
|
+
"invalid_json",
|
|
184
|
+
"body must be valid UTF-8 JSON",
|
|
185
|
+
) from exc
|
|
186
|
+
|
|
187
|
+
def do_GET(self) -> None:
|
|
188
|
+
if not self._accept_host():
|
|
189
|
+
return
|
|
190
|
+
parsed = urlsplit(self.path)
|
|
191
|
+
path = parsed.path
|
|
192
|
+
if path == "/api/health":
|
|
193
|
+
self._send_json(
|
|
194
|
+
{
|
|
195
|
+
"ok": True,
|
|
196
|
+
"service": "agentinputlab-studio",
|
|
197
|
+
"version": 1,
|
|
198
|
+
"execution": "local-only",
|
|
199
|
+
"network_runs": bool(
|
|
200
|
+
self.server.catalog["execution_policy"]["network_enabled"]
|
|
201
|
+
),
|
|
202
|
+
"campaign_mode_configured": self.server.campaign_manager is not None,
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
return
|
|
206
|
+
if path == "/api/catalog":
|
|
207
|
+
self._send_json(self.server.catalog)
|
|
208
|
+
return
|
|
209
|
+
if path == "/api/campaigns/profiles":
|
|
210
|
+
campaigns = self.server.campaign_manager
|
|
211
|
+
self._send_json(
|
|
212
|
+
campaigns.catalog_payload()
|
|
213
|
+
if campaigns is not None
|
|
214
|
+
else disabled_campaign_catalog_payload()
|
|
215
|
+
)
|
|
216
|
+
return
|
|
217
|
+
if path == "/api/campaigns/executions":
|
|
218
|
+
campaigns = self.server.campaign_manager
|
|
219
|
+
self._send_json(
|
|
220
|
+
{
|
|
221
|
+
"configured": campaigns is not None,
|
|
222
|
+
"executions": (campaigns.list_executions() if campaigns is not None else []),
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
return
|
|
226
|
+
if path.startswith("/api/campaigns/executions/"):
|
|
227
|
+
execution_id = path.removeprefix("/api/campaigns/executions/")
|
|
228
|
+
campaigns = self.server.campaign_manager
|
|
229
|
+
record = (
|
|
230
|
+
campaigns.get_execution(execution_id)
|
|
231
|
+
if campaigns is not None and RUN_ID.fullmatch(execution_id)
|
|
232
|
+
else None
|
|
233
|
+
)
|
|
234
|
+
if record is None:
|
|
235
|
+
self._send_error(
|
|
236
|
+
HTTPStatus.NOT_FOUND,
|
|
237
|
+
"campaign_execution_not_found",
|
|
238
|
+
"campaign execution does not exist",
|
|
239
|
+
)
|
|
240
|
+
return
|
|
241
|
+
self._send_json(record)
|
|
242
|
+
return
|
|
243
|
+
if path.startswith("/api/campaigns/preflights/"):
|
|
244
|
+
preparation_id = path.removeprefix("/api/campaigns/preflights/")
|
|
245
|
+
campaigns = self.server.campaign_manager
|
|
246
|
+
record = (
|
|
247
|
+
campaigns.get_preparation(preparation_id)
|
|
248
|
+
if campaigns is not None and RUN_ID.fullmatch(preparation_id)
|
|
249
|
+
else None
|
|
250
|
+
)
|
|
251
|
+
if record is None:
|
|
252
|
+
self._send_error(
|
|
253
|
+
HTTPStatus.NOT_FOUND,
|
|
254
|
+
"campaign_preflight_not_found",
|
|
255
|
+
"campaign preflight does not exist",
|
|
256
|
+
)
|
|
257
|
+
return
|
|
258
|
+
self._send_json(record)
|
|
259
|
+
return
|
|
260
|
+
if path == "/api/runs":
|
|
261
|
+
self._send_json({"runs": self.server.manager.list_runs()})
|
|
262
|
+
return
|
|
263
|
+
if path.startswith("/api/runs/"):
|
|
264
|
+
run_id = path.removeprefix("/api/runs/")
|
|
265
|
+
if not RUN_ID.fullmatch(run_id):
|
|
266
|
+
self._send_error(HTTPStatus.NOT_FOUND, "run_not_found", "run does not exist")
|
|
267
|
+
return
|
|
268
|
+
record = self.server.manager.get(run_id)
|
|
269
|
+
if record is None:
|
|
270
|
+
self._send_error(HTTPStatus.NOT_FOUND, "run_not_found", "run does not exist")
|
|
271
|
+
return
|
|
272
|
+
self._send_json(record)
|
|
273
|
+
return
|
|
274
|
+
if path in STATIC_ROUTES:
|
|
275
|
+
self._send_static(STATIC_ROUTES[path])
|
|
276
|
+
return
|
|
277
|
+
self._send_error(HTTPStatus.NOT_FOUND, "not_found", "route does not exist")
|
|
278
|
+
|
|
279
|
+
def do_POST(self) -> None:
|
|
280
|
+
if not self._accept_host():
|
|
281
|
+
return
|
|
282
|
+
path = urlsplit(self.path).path
|
|
283
|
+
if path not in {
|
|
284
|
+
"/api/validate",
|
|
285
|
+
"/api/runs",
|
|
286
|
+
"/api/campaigns/preflight",
|
|
287
|
+
"/api/campaigns/executions",
|
|
288
|
+
}:
|
|
289
|
+
self._send_error(HTTPStatus.NOT_FOUND, "not_found", "route does not exist")
|
|
290
|
+
return
|
|
291
|
+
if path.startswith("/api/campaigns/"):
|
|
292
|
+
self._handle_campaign_post(path)
|
|
293
|
+
return
|
|
294
|
+
try:
|
|
295
|
+
payload = self._read_json()
|
|
296
|
+
report = validate_config(payload, catalog=self.server.catalog)
|
|
297
|
+
except _HTTPInputError as exc:
|
|
298
|
+
self._send_error(exc.status, exc.code, exc.message)
|
|
299
|
+
return
|
|
300
|
+
except StudioSecurityError as exc:
|
|
301
|
+
self._send_error(HTTPStatus.FORBIDDEN, "credentials_rejected", str(exc))
|
|
302
|
+
return
|
|
303
|
+
if path == "/api/validate":
|
|
304
|
+
self._send_json(report)
|
|
305
|
+
return
|
|
306
|
+
if not report["valid"]:
|
|
307
|
+
self._send_json(report, status=HTTPStatus.UNPROCESSABLE_ENTITY)
|
|
308
|
+
return
|
|
309
|
+
try:
|
|
310
|
+
run = self.server.manager.submit(payload)
|
|
311
|
+
except StudioQueueFullError as exc:
|
|
312
|
+
self._send_error(HTTPStatus.TOO_MANY_REQUESTS, "queue_full", str(exc))
|
|
313
|
+
return
|
|
314
|
+
except StudioValidationError as exc:
|
|
315
|
+
self._send_error(HTTPStatus.UNPROCESSABLE_ENTITY, "invalid_config", str(exc))
|
|
316
|
+
return
|
|
317
|
+
self._send_json(run, status=HTTPStatus.ACCEPTED)
|
|
318
|
+
|
|
319
|
+
def _handle_campaign_post(self, path: str) -> None:
|
|
320
|
+
campaigns = self.server.campaign_manager
|
|
321
|
+
if campaigns is None:
|
|
322
|
+
self._send_error(
|
|
323
|
+
HTTPStatus.CONFLICT,
|
|
324
|
+
"campaigns_not_configured",
|
|
325
|
+
"Studio was not started with a campaign profile catalog",
|
|
326
|
+
)
|
|
327
|
+
return
|
|
328
|
+
try:
|
|
329
|
+
payload = self._read_json()
|
|
330
|
+
if path == "/api/campaigns/preflight":
|
|
331
|
+
self._send_json(campaigns.prepare(payload))
|
|
332
|
+
return
|
|
333
|
+
self._send_json(
|
|
334
|
+
campaigns.submit_execution(payload),
|
|
335
|
+
status=HTTPStatus.ACCEPTED,
|
|
336
|
+
)
|
|
337
|
+
except _HTTPInputError as exc:
|
|
338
|
+
self._send_error(exc.status, exc.code, exc.message)
|
|
339
|
+
except CampaignSecurityError as exc:
|
|
340
|
+
self._send_error(
|
|
341
|
+
HTTPStatus.FORBIDDEN,
|
|
342
|
+
"campaign_input_rejected",
|
|
343
|
+
str(exc),
|
|
344
|
+
)
|
|
345
|
+
except CampaignQueueFullError as exc:
|
|
346
|
+
self._send_error(HTTPStatus.TOO_MANY_REQUESTS, "queue_full", str(exc))
|
|
347
|
+
except CampaignValidationError as exc:
|
|
348
|
+
self._send_error(
|
|
349
|
+
HTTPStatus.UNPROCESSABLE_ENTITY,
|
|
350
|
+
"campaign_invalid",
|
|
351
|
+
str(exc),
|
|
352
|
+
)
|
|
353
|
+
except (ArtifactError, ValidationError):
|
|
354
|
+
self._send_error(
|
|
355
|
+
HTTPStatus.UNPROCESSABLE_ENTITY,
|
|
356
|
+
"campaign_preflight_failed",
|
|
357
|
+
"Campaign profile attestation or preflight failed",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
def do_OPTIONS(self) -> None:
|
|
361
|
+
if not self._accept_host():
|
|
362
|
+
return
|
|
363
|
+
self.send_response(HTTPStatus.METHOD_NOT_ALLOWED)
|
|
364
|
+
self.send_header("Allow", "GET, POST")
|
|
365
|
+
self.send_header("Content-Length", "0")
|
|
366
|
+
self._security_headers()
|
|
367
|
+
self.end_headers()
|
|
368
|
+
|
|
369
|
+
def _send_static(self, filename: str) -> None:
|
|
370
|
+
path = STATIC_DIR / filename
|
|
371
|
+
try:
|
|
372
|
+
data = path.read_bytes()
|
|
373
|
+
except OSError:
|
|
374
|
+
self._send_error(HTTPStatus.NOT_FOUND, "asset_not_found", "asset does not exist")
|
|
375
|
+
return
|
|
376
|
+
content_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
|
377
|
+
if content_type.startswith("text/") or content_type == "application/javascript":
|
|
378
|
+
content_type = f"{content_type}; charset=utf-8"
|
|
379
|
+
self.send_response(HTTPStatus.OK)
|
|
380
|
+
self.send_header("Content-Type", content_type)
|
|
381
|
+
self.send_header("Content-Length", str(len(data)))
|
|
382
|
+
self.send_header("Cache-Control", "no-cache")
|
|
383
|
+
self._security_headers()
|
|
384
|
+
self.end_headers()
|
|
385
|
+
self.wfile.write(data)
|
|
386
|
+
|
|
387
|
+
def log_message(self, format: str, *args: Any) -> None:
|
|
388
|
+
# The handler never logs request bodies or headers, which keeps secrets
|
|
389
|
+
# out of terminal output even when a client sends a rejected payload.
|
|
390
|
+
sys.stderr.write(
|
|
391
|
+
f"studio {self.client_address[0]} {self.command} "
|
|
392
|
+
f"{urlsplit(self.path).path} — {format % args}\n"
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class _HTTPInputError(Exception):
|
|
397
|
+
def __init__(self, status: HTTPStatus, code: str, message: str) -> None:
|
|
398
|
+
super().__init__(message)
|
|
399
|
+
self.status = status
|
|
400
|
+
self.code = code
|
|
401
|
+
self.message = message
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def create_server(
|
|
405
|
+
host: str = "127.0.0.1",
|
|
406
|
+
port: int = 7861,
|
|
407
|
+
*,
|
|
408
|
+
max_workers: int = 1,
|
|
409
|
+
manager: RunManager | None = None,
|
|
410
|
+
prepared_data_root: str | Path | None = None,
|
|
411
|
+
campaign_manager: CampaignManager | None = None,
|
|
412
|
+
campaign_catalog_path: str | Path | None = None,
|
|
413
|
+
campaign_output_root: str | Path | None = None,
|
|
414
|
+
campaign_workers: int = 1,
|
|
415
|
+
model_backend_gate: StudioModelBackendGate | None = None,
|
|
416
|
+
model_backend_config_path: str | Path | None = None,
|
|
417
|
+
model_backend_config_sha256: str | None = None,
|
|
418
|
+
allow_remote: bool = False,
|
|
419
|
+
) -> StudioHTTPServer:
|
|
420
|
+
if host not in LOOPBACK_HOSTS and not allow_remote:
|
|
421
|
+
raise ValueError(
|
|
422
|
+
"refusing non-loopback bind without allow_remote=True; Studio has no authentication"
|
|
423
|
+
)
|
|
424
|
+
if manager is not None and prepared_data_root is not None:
|
|
425
|
+
raise ValueError("prepared_data_root must be configured on the supplied RunManager")
|
|
426
|
+
if manager is not None and (
|
|
427
|
+
model_backend_gate is not None
|
|
428
|
+
or model_backend_config_path is not None
|
|
429
|
+
or model_backend_config_sha256 is not None
|
|
430
|
+
):
|
|
431
|
+
raise ValueError("model backend gate must be configured on the supplied RunManager")
|
|
432
|
+
if model_backend_gate is not None and (
|
|
433
|
+
model_backend_config_path is not None or model_backend_config_sha256 is not None
|
|
434
|
+
):
|
|
435
|
+
raise ValueError("supply either model_backend_gate or operator config path/SHA, not both")
|
|
436
|
+
if campaign_manager is not None and (
|
|
437
|
+
campaign_catalog_path is not None or campaign_output_root is not None
|
|
438
|
+
):
|
|
439
|
+
raise ValueError(
|
|
440
|
+
"campaign catalog/output must be configured on the supplied CampaignManager"
|
|
441
|
+
)
|
|
442
|
+
if campaign_catalog_path is None and campaign_output_root is not None:
|
|
443
|
+
raise ValueError("campaign_output_root requires campaign_catalog_path")
|
|
444
|
+
operator_backend_gate = model_backend_gate or StudioModelBackendGate.from_operator_config(
|
|
445
|
+
model_backend_config_path,
|
|
446
|
+
model_backend_config_sha256,
|
|
447
|
+
)
|
|
448
|
+
run_manager = manager or RunManager(
|
|
449
|
+
max_workers=max_workers,
|
|
450
|
+
prepared_data_root=prepared_data_root,
|
|
451
|
+
model_backend_gate=operator_backend_gate,
|
|
452
|
+
)
|
|
453
|
+
campaigns = campaign_manager
|
|
454
|
+
owns_campaigns = campaigns is None and campaign_catalog_path is not None
|
|
455
|
+
try:
|
|
456
|
+
if owns_campaigns:
|
|
457
|
+
campaigns = CampaignManager(
|
|
458
|
+
catalog_path=campaign_catalog_path,
|
|
459
|
+
output_root=campaign_output_root,
|
|
460
|
+
max_workers=campaign_workers,
|
|
461
|
+
)
|
|
462
|
+
return StudioHTTPServer(
|
|
463
|
+
(host, port),
|
|
464
|
+
StudioHandler,
|
|
465
|
+
manager=run_manager,
|
|
466
|
+
campaign_manager=campaigns,
|
|
467
|
+
)
|
|
468
|
+
except Exception:
|
|
469
|
+
if manager is None:
|
|
470
|
+
run_manager.shutdown(wait=True)
|
|
471
|
+
if owns_campaigns and campaigns is not None:
|
|
472
|
+
campaigns.shutdown(wait=True)
|
|
473
|
+
raise
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
477
|
+
parser = argparse.ArgumentParser(description="Run the local AgentInputLab experiment Studio.")
|
|
478
|
+
parser.add_argument("--host", default="127.0.0.1", help="Bind host (loopback by default).")
|
|
479
|
+
parser.add_argument("--port", type=int, default=7861, help="Bind port.")
|
|
480
|
+
parser.add_argument("--workers", type=int, default=1, choices=range(1, 5))
|
|
481
|
+
parser.add_argument(
|
|
482
|
+
"--prepared-data-root",
|
|
483
|
+
type=Path,
|
|
484
|
+
default=(
|
|
485
|
+
Path(os.environ["AGENTINPUTLAB_PREPARED_DATA_ROOT"])
|
|
486
|
+
if os.environ.get("AGENTINPUTLAB_PREPARED_DATA_ROOT")
|
|
487
|
+
else None
|
|
488
|
+
),
|
|
489
|
+
help=(
|
|
490
|
+
"Server-controlled prepared benchmark cache root. "
|
|
491
|
+
"May also be set with AGENTINPUTLAB_PREPARED_DATA_ROOT."
|
|
492
|
+
),
|
|
493
|
+
)
|
|
494
|
+
parser.add_argument(
|
|
495
|
+
"--allow-remote",
|
|
496
|
+
action="store_true",
|
|
497
|
+
help="Acknowledge that remote binding has no authentication and is unsafe on public networks.",
|
|
498
|
+
)
|
|
499
|
+
parser.add_argument(
|
|
500
|
+
"--campaign-catalog",
|
|
501
|
+
type=Path,
|
|
502
|
+
default=(
|
|
503
|
+
Path(os.environ["AGENTINPUTLAB_CAMPAIGN_CATALOG"])
|
|
504
|
+
if os.environ.get("AGENTINPUTLAB_CAMPAIGN_CATALOG")
|
|
505
|
+
else None
|
|
506
|
+
),
|
|
507
|
+
help=(
|
|
508
|
+
"Server-controlled, checksum-locked campaign profile catalog. "
|
|
509
|
+
"Never accepted from a browser request."
|
|
510
|
+
),
|
|
511
|
+
)
|
|
512
|
+
parser.add_argument(
|
|
513
|
+
"--campaign-output-root",
|
|
514
|
+
type=Path,
|
|
515
|
+
default=(
|
|
516
|
+
Path(os.environ["AGENTINPUTLAB_CAMPAIGN_OUTPUT_ROOT"])
|
|
517
|
+
if os.environ.get("AGENTINPUTLAB_CAMPAIGN_OUTPUT_ROOT")
|
|
518
|
+
else None
|
|
519
|
+
),
|
|
520
|
+
help=(
|
|
521
|
+
"Server-controlled campaign artifact root. Without it, campaign "
|
|
522
|
+
"profiles remain preflight/plan only."
|
|
523
|
+
),
|
|
524
|
+
)
|
|
525
|
+
parser.add_argument(
|
|
526
|
+
"--campaign-workers",
|
|
527
|
+
type=int,
|
|
528
|
+
default=1,
|
|
529
|
+
choices=range(1, 5),
|
|
530
|
+
help="Bounded campaign workers, separate from tiny Studio runs.",
|
|
531
|
+
)
|
|
532
|
+
parser.add_argument(
|
|
533
|
+
"--model-backend-config",
|
|
534
|
+
type=Path,
|
|
535
|
+
default=(
|
|
536
|
+
Path(os.environ[STUDIO_MODEL_BACKEND_CONFIG_ENV])
|
|
537
|
+
if os.environ.get(STUDIO_MODEL_BACKEND_CONFIG_ENV)
|
|
538
|
+
else None
|
|
539
|
+
),
|
|
540
|
+
help=(
|
|
541
|
+
"Server-controlled ProductionBackendBuilder parameter file. "
|
|
542
|
+
f"May also be set with {STUDIO_MODEL_BACKEND_CONFIG_ENV}; never "
|
|
543
|
+
"accepted from a browser request."
|
|
544
|
+
),
|
|
545
|
+
)
|
|
546
|
+
parser.add_argument(
|
|
547
|
+
"--model-backend-config-sha256",
|
|
548
|
+
default=os.environ.get(STUDIO_MODEL_BACKEND_CONFIG_SHA256_ENV),
|
|
549
|
+
help=(
|
|
550
|
+
"Exact SHA-256 of --model-backend-config. "
|
|
551
|
+
f"May also be set with {STUDIO_MODEL_BACKEND_CONFIG_SHA256_ENV}."
|
|
552
|
+
),
|
|
553
|
+
)
|
|
554
|
+
return parser
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def main(argv: list[str] | None = None) -> int:
|
|
558
|
+
args = build_parser().parse_args(argv)
|
|
559
|
+
if args.host not in LOOPBACK_HOSTS and not args.allow_remote:
|
|
560
|
+
print(
|
|
561
|
+
"refusing non-loopback bind without --allow-remote; Studio has no authentication",
|
|
562
|
+
file=sys.stderr,
|
|
563
|
+
)
|
|
564
|
+
return 2
|
|
565
|
+
server = create_server(
|
|
566
|
+
args.host,
|
|
567
|
+
args.port,
|
|
568
|
+
max_workers=args.workers,
|
|
569
|
+
prepared_data_root=args.prepared_data_root,
|
|
570
|
+
campaign_catalog_path=args.campaign_catalog,
|
|
571
|
+
campaign_output_root=args.campaign_output_root,
|
|
572
|
+
campaign_workers=args.campaign_workers,
|
|
573
|
+
model_backend_config_path=args.model_backend_config,
|
|
574
|
+
model_backend_config_sha256=args.model_backend_config_sha256,
|
|
575
|
+
allow_remote=args.allow_remote,
|
|
576
|
+
)
|
|
577
|
+
host, port = server.server_address[:2]
|
|
578
|
+
print(f"AgentInputLab Studio listening on http://{host}:{port}")
|
|
579
|
+
model_backend_id = server.catalog["execution_policy"]["operator_model_backend_id"]
|
|
580
|
+
if model_backend_id is None:
|
|
581
|
+
print("Local execution only · credentials rejected · operator model backend locked")
|
|
582
|
+
else:
|
|
583
|
+
print(
|
|
584
|
+
"Local execution only · credentials rejected · "
|
|
585
|
+
f"operator model backend ready: {model_backend_id}"
|
|
586
|
+
)
|
|
587
|
+
verified_cells = server.catalog["execution_policy"]["verified_prepared_cells"]
|
|
588
|
+
if verified_cells:
|
|
589
|
+
print(f"Verified prepared benchmark cells: {', '.join(verified_cells)}")
|
|
590
|
+
if server.campaign_manager is None:
|
|
591
|
+
print("Research campaigns: disabled (no server-side profile catalog)")
|
|
592
|
+
else:
|
|
593
|
+
profiles = server.campaign_manager.catalog_payload()["profiles"]
|
|
594
|
+
mode = (
|
|
595
|
+
"preflight + confirmed execution"
|
|
596
|
+
if args.campaign_output_root is not None
|
|
597
|
+
else "preflight/plan only"
|
|
598
|
+
)
|
|
599
|
+
print(f"Research campaigns: {len(profiles)} allowlisted profiles · {mode}")
|
|
600
|
+
try:
|
|
601
|
+
server.serve_forever(poll_interval=0.25)
|
|
602
|
+
except KeyboardInterrupt:
|
|
603
|
+
print("\nStopping Studio")
|
|
604
|
+
finally:
|
|
605
|
+
server.server_close()
|
|
606
|
+
return 0
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
if __name__ == "__main__":
|
|
610
|
+
raise SystemExit(main())
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
__all__ = ["MAX_REQUEST_BYTES", "StudioHTTPServer", "create_server", "main"]
|