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,514 @@
|
|
|
1
|
+
"""Strict, execution-neutral schemas for reproducible experiment manifests.
|
|
2
|
+
|
|
3
|
+
The schemas in this module describe work without performing it. In
|
|
4
|
+
particular, loading a manifest never imports an operator driver, downloads an
|
|
5
|
+
asset, opens a model endpoint, or creates a result directory.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from collections.abc import Mapping, Sequence
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from agentinputlab.contracts.enums import ProfileKind, SemanticFamily, StrEnum
|
|
16
|
+
from agentinputlab.contracts.errors import ValidationError
|
|
17
|
+
from agentinputlab.contracts.serialization import content_hash, freeze_mapping, to_jsonable
|
|
18
|
+
|
|
19
|
+
_IDENTIFIER = re.compile(r"^[a-z0-9][a-z0-9_.:-]*$")
|
|
20
|
+
_ENVIRONMENT_NAME = re.compile(r"^[A-Z_][A-Z0-9_]*$")
|
|
21
|
+
_ENTRYPOINT = re.compile(r"^[A-Za-z_][A-Za-z0-9_.]*:[A-Za-z_][A-Za-z0-9_.]*$")
|
|
22
|
+
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ExperimentStage(StrEnum):
|
|
26
|
+
PARITY = "parity"
|
|
27
|
+
SMOKE = "smoke"
|
|
28
|
+
DISCOVERY = "discovery"
|
|
29
|
+
CONFIRMATORY = "confirmatory"
|
|
30
|
+
SELECTOR_CONFIRMATION = "selector_confirmation"
|
|
31
|
+
REPLICATION = "replication"
|
|
32
|
+
TRANSFER = "transfer"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PrerequisiteKind(StrEnum):
|
|
36
|
+
"""Kinds that can be checked without executing an experiment."""
|
|
37
|
+
|
|
38
|
+
ENV = "env"
|
|
39
|
+
FILE_ENV = "file_env"
|
|
40
|
+
DIRECTORY_ENV = "directory_env"
|
|
41
|
+
EXECUTABLE = "executable"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ExperimentArmKind(StrEnum):
|
|
45
|
+
"""Whether an arm binds one release or an already-frozen policy."""
|
|
46
|
+
|
|
47
|
+
RELEASE = "release"
|
|
48
|
+
POLICY = "policy"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True, slots=True)
|
|
52
|
+
class ExperimentPrerequisite:
|
|
53
|
+
kind: PrerequisiteKind
|
|
54
|
+
name: str
|
|
55
|
+
description: str
|
|
56
|
+
sha256_env: str | None = None
|
|
57
|
+
|
|
58
|
+
def __post_init__(self) -> None:
|
|
59
|
+
if not isinstance(self.kind, PrerequisiteKind):
|
|
60
|
+
raise ValidationError("experiment prerequisite kind must be typed")
|
|
61
|
+
if not isinstance(self.name, str) or not self.name.strip():
|
|
62
|
+
raise ValidationError("experiment prerequisite name must be non-empty")
|
|
63
|
+
if not isinstance(self.description, str) or not self.description.strip():
|
|
64
|
+
raise ValidationError("experiment prerequisite description must be non-empty")
|
|
65
|
+
if self.kind is not PrerequisiteKind.EXECUTABLE and not _ENVIRONMENT_NAME.fullmatch(
|
|
66
|
+
self.name
|
|
67
|
+
):
|
|
68
|
+
raise ValidationError("environment prerequisite names must be uppercase identifiers")
|
|
69
|
+
if self.sha256_env is not None:
|
|
70
|
+
if self.kind is not PrerequisiteKind.FILE_ENV:
|
|
71
|
+
raise ValidationError("sha256_env is valid only for file_env prerequisites")
|
|
72
|
+
if not _ENVIRONMENT_NAME.fullmatch(self.sha256_env):
|
|
73
|
+
raise ValidationError("sha256_env must be an uppercase environment identifier")
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_mapping(cls, value: Any, *, path: str) -> ExperimentPrerequisite:
|
|
77
|
+
data = _strict_mapping(
|
|
78
|
+
value,
|
|
79
|
+
path=path,
|
|
80
|
+
keys={"kind", "name", "description", "sha256_env"},
|
|
81
|
+
required={"kind", "name", "description"},
|
|
82
|
+
)
|
|
83
|
+
return cls(
|
|
84
|
+
kind=_enum(PrerequisiteKind, data["kind"], f"{path}.kind"),
|
|
85
|
+
name=_text(data["name"], f"{path}.name"),
|
|
86
|
+
description=_text(data["description"], f"{path}.description"),
|
|
87
|
+
sha256_env=_optional_text(data.get("sha256_env"), f"{path}.sha256_env"),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True, slots=True)
|
|
92
|
+
class ExperimentBenchmarkTarget:
|
|
93
|
+
"""One benchmark split plus an exact or operator-supplied unit inventory."""
|
|
94
|
+
|
|
95
|
+
cell_id: str
|
|
96
|
+
split: str
|
|
97
|
+
unit_ids: tuple[str, ...] = ()
|
|
98
|
+
unit_inventory_env: str | None = None
|
|
99
|
+
unit_inventory_sha256_env: str | None = None
|
|
100
|
+
external: bool = False
|
|
101
|
+
source_revision: str | None = None
|
|
102
|
+
arm_ids: tuple[str, ...] = ()
|
|
103
|
+
|
|
104
|
+
def __post_init__(self) -> None:
|
|
105
|
+
_identity(self.cell_id, label="benchmark cell_id")
|
|
106
|
+
_identity(self.split, label="benchmark split")
|
|
107
|
+
units = tuple(self.unit_ids)
|
|
108
|
+
if any(not isinstance(item, str) or not item.strip() for item in units):
|
|
109
|
+
raise ValidationError("benchmark unit IDs must be non-empty strings")
|
|
110
|
+
if len(units) != len(set(units)):
|
|
111
|
+
raise ValidationError("benchmark unit IDs must be unique")
|
|
112
|
+
if bool(units) == bool(self.unit_inventory_env):
|
|
113
|
+
raise ValidationError(
|
|
114
|
+
"benchmark target requires exactly one of unit_ids or unit_inventory_env"
|
|
115
|
+
)
|
|
116
|
+
if self.unit_inventory_env is not None and not _ENVIRONMENT_NAME.fullmatch(
|
|
117
|
+
self.unit_inventory_env
|
|
118
|
+
):
|
|
119
|
+
raise ValidationError("unit_inventory_env must be an uppercase identifier")
|
|
120
|
+
if self.unit_inventory_sha256_env is not None:
|
|
121
|
+
if self.unit_inventory_env is None:
|
|
122
|
+
raise ValidationError("unit_inventory_sha256_env requires unit_inventory_env")
|
|
123
|
+
if not _ENVIRONMENT_NAME.fullmatch(self.unit_inventory_sha256_env):
|
|
124
|
+
raise ValidationError("unit_inventory_sha256_env must be an uppercase identifier")
|
|
125
|
+
if not isinstance(self.external, bool):
|
|
126
|
+
raise ValidationError("benchmark external flag must be boolean")
|
|
127
|
+
if self.external and (
|
|
128
|
+
not isinstance(self.source_revision, str) or not self.source_revision.strip()
|
|
129
|
+
):
|
|
130
|
+
raise ValidationError("external benchmark targets require source_revision")
|
|
131
|
+
if not self.external and self.source_revision is not None:
|
|
132
|
+
raise ValidationError("registered benchmark revisions come from the benchmark registry")
|
|
133
|
+
arm_ids = tuple(self.arm_ids)
|
|
134
|
+
if arm_ids:
|
|
135
|
+
if len(arm_ids) < 2:
|
|
136
|
+
raise ValidationError(
|
|
137
|
+
"benchmark-specific arm_ids require at least two comparison arms"
|
|
138
|
+
)
|
|
139
|
+
if len(arm_ids) != len(set(arm_ids)):
|
|
140
|
+
raise ValidationError("benchmark-specific arm_ids must be unique")
|
|
141
|
+
for arm_id in arm_ids:
|
|
142
|
+
_identity(arm_id, label="benchmark-specific arm_id")
|
|
143
|
+
object.__setattr__(self, "unit_ids", units)
|
|
144
|
+
object.__setattr__(self, "arm_ids", arm_ids)
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def from_mapping(cls, value: Any, *, path: str) -> ExperimentBenchmarkTarget:
|
|
148
|
+
data = _strict_mapping(
|
|
149
|
+
value,
|
|
150
|
+
path=path,
|
|
151
|
+
keys={
|
|
152
|
+
"cell_id",
|
|
153
|
+
"split",
|
|
154
|
+
"unit_ids",
|
|
155
|
+
"unit_inventory_env",
|
|
156
|
+
"unit_inventory_sha256_env",
|
|
157
|
+
"external",
|
|
158
|
+
"source_revision",
|
|
159
|
+
"arm_ids",
|
|
160
|
+
},
|
|
161
|
+
required={"cell_id", "split"},
|
|
162
|
+
)
|
|
163
|
+
return cls(
|
|
164
|
+
cell_id=_text(data["cell_id"], f"{path}.cell_id"),
|
|
165
|
+
split=_text(data["split"], f"{path}.split"),
|
|
166
|
+
unit_ids=_string_tuple(data.get("unit_ids", []), f"{path}.unit_ids"),
|
|
167
|
+
unit_inventory_env=_optional_text(
|
|
168
|
+
data.get("unit_inventory_env"),
|
|
169
|
+
f"{path}.unit_inventory_env",
|
|
170
|
+
),
|
|
171
|
+
unit_inventory_sha256_env=_optional_text(
|
|
172
|
+
data.get("unit_inventory_sha256_env"),
|
|
173
|
+
f"{path}.unit_inventory_sha256_env",
|
|
174
|
+
),
|
|
175
|
+
external=_boolean(data.get("external", False), f"{path}.external"),
|
|
176
|
+
source_revision=_optional_text(
|
|
177
|
+
data.get("source_revision"),
|
|
178
|
+
f"{path}.source_revision",
|
|
179
|
+
),
|
|
180
|
+
arm_ids=_string_tuple(data.get("arm_ids", []), f"{path}.arm_ids"),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@dataclass(frozen=True, slots=True)
|
|
185
|
+
class ExperimentArmSpec:
|
|
186
|
+
arm_id: str
|
|
187
|
+
release_id: str
|
|
188
|
+
family: SemanticFamily
|
|
189
|
+
profile_kind: ProfileKind
|
|
190
|
+
arm_kind: ExperimentArmKind = ExperimentArmKind.RELEASE
|
|
191
|
+
parameters: Mapping[str, Any] = field(default_factory=dict)
|
|
192
|
+
|
|
193
|
+
def __post_init__(self) -> None:
|
|
194
|
+
_identity(self.arm_id, label="experiment arm_id")
|
|
195
|
+
_identity(self.release_id, label="experiment release_id")
|
|
196
|
+
if not isinstance(self.family, SemanticFamily):
|
|
197
|
+
raise ValidationError("experiment arm family must be typed")
|
|
198
|
+
if not isinstance(self.profile_kind, ProfileKind):
|
|
199
|
+
raise ValidationError("experiment arm profile_kind must be typed")
|
|
200
|
+
if not isinstance(self.arm_kind, ExperimentArmKind):
|
|
201
|
+
raise ValidationError("experiment arm_kind must be typed")
|
|
202
|
+
if self.arm_kind is ExperimentArmKind.POLICY:
|
|
203
|
+
if self.family is not SemanticFamily.COMPOSITE:
|
|
204
|
+
raise ValidationError("experiment policy arms must use family=composite")
|
|
205
|
+
if not self.release_id.startswith("policy."):
|
|
206
|
+
raise ValidationError("experiment policy arm identity must start with policy.")
|
|
207
|
+
elif self.release_id.startswith("policy."):
|
|
208
|
+
raise ValidationError("policy.* identities require arm_kind=policy")
|
|
209
|
+
try:
|
|
210
|
+
frozen = freeze_mapping(self.parameters)
|
|
211
|
+
except (TypeError, ValueError) as exc:
|
|
212
|
+
raise ValidationError("experiment arm parameters must be canonical JSON") from exc
|
|
213
|
+
object.__setattr__(self, "parameters", frozen)
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
def from_mapping(cls, value: Any, *, path: str) -> ExperimentArmSpec:
|
|
217
|
+
data = _strict_mapping(
|
|
218
|
+
value,
|
|
219
|
+
path=path,
|
|
220
|
+
keys={
|
|
221
|
+
"arm_id",
|
|
222
|
+
"release_id",
|
|
223
|
+
"family",
|
|
224
|
+
"profile_kind",
|
|
225
|
+
"arm_kind",
|
|
226
|
+
"parameters",
|
|
227
|
+
},
|
|
228
|
+
required={"arm_id", "release_id", "family", "profile_kind"},
|
|
229
|
+
)
|
|
230
|
+
parameters = data.get("parameters", {})
|
|
231
|
+
if not isinstance(parameters, Mapping):
|
|
232
|
+
raise ValidationError(f"{path}.parameters must be an object")
|
|
233
|
+
return cls(
|
|
234
|
+
arm_id=_text(data["arm_id"], f"{path}.arm_id"),
|
|
235
|
+
release_id=_text(data["release_id"], f"{path}.release_id"),
|
|
236
|
+
family=_enum(SemanticFamily, data["family"], f"{path}.family"),
|
|
237
|
+
profile_kind=_enum(ProfileKind, data["profile_kind"], f"{path}.profile_kind"),
|
|
238
|
+
arm_kind=_enum(
|
|
239
|
+
ExperimentArmKind,
|
|
240
|
+
data.get("arm_kind", ExperimentArmKind.RELEASE.value),
|
|
241
|
+
f"{path}.arm_kind",
|
|
242
|
+
),
|
|
243
|
+
parameters=parameters,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@dataclass(frozen=True, slots=True)
|
|
248
|
+
class ExperimentManifest:
|
|
249
|
+
"""Frozen operator intent for one experiment family."""
|
|
250
|
+
|
|
251
|
+
schema_version: int
|
|
252
|
+
study_id: str
|
|
253
|
+
stage: ExperimentStage
|
|
254
|
+
estimand_profile_kind: ProfileKind
|
|
255
|
+
benchmarks: tuple[ExperimentBenchmarkTarget, ...]
|
|
256
|
+
arms: tuple[ExperimentArmSpec, ...]
|
|
257
|
+
seeds: tuple[int, ...]
|
|
258
|
+
model_slots: tuple[str, ...] = ("default",)
|
|
259
|
+
driver_entrypoint: str | None = None
|
|
260
|
+
prerequisites: tuple[ExperimentPrerequisite, ...] = ()
|
|
261
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
262
|
+
|
|
263
|
+
def __post_init__(self) -> None:
|
|
264
|
+
if self.schema_version != 1:
|
|
265
|
+
raise ValidationError("unsupported experiment manifest schema_version")
|
|
266
|
+
_identity(self.study_id, label="experiment study_id")
|
|
267
|
+
if not isinstance(self.stage, ExperimentStage):
|
|
268
|
+
raise ValidationError("experiment stage must be typed")
|
|
269
|
+
if not isinstance(self.estimand_profile_kind, ProfileKind):
|
|
270
|
+
raise ValidationError("estimand_profile_kind must be typed")
|
|
271
|
+
benchmarks = tuple(self.benchmarks)
|
|
272
|
+
arms = tuple(self.arms)
|
|
273
|
+
seeds = tuple(self.seeds)
|
|
274
|
+
model_slots = tuple(self.model_slots)
|
|
275
|
+
prerequisites = tuple(self.prerequisites)
|
|
276
|
+
if not benchmarks or not all(
|
|
277
|
+
isinstance(item, ExperimentBenchmarkTarget) for item in benchmarks
|
|
278
|
+
):
|
|
279
|
+
raise ValidationError("experiment manifest requires benchmark targets")
|
|
280
|
+
if len({(item.cell_id, item.split) for item in benchmarks}) != len(benchmarks):
|
|
281
|
+
raise ValidationError("experiment benchmark target identities must be unique")
|
|
282
|
+
if len(arms) < 2 or not all(isinstance(item, ExperimentArmSpec) for item in arms):
|
|
283
|
+
raise ValidationError("experiment manifest requires at least two arms")
|
|
284
|
+
if len({item.arm_id for item in arms}) != len(arms):
|
|
285
|
+
raise ValidationError("experiment arm IDs must be unique")
|
|
286
|
+
declared_arm_ids = {item.arm_id for item in arms}
|
|
287
|
+
for benchmark in benchmarks:
|
|
288
|
+
unknown_arm_ids = set(benchmark.arm_ids) - declared_arm_ids
|
|
289
|
+
if unknown_arm_ids:
|
|
290
|
+
raise ValidationError(
|
|
291
|
+
f"benchmark {benchmark.cell_id} references unknown arm IDs: "
|
|
292
|
+
+ ", ".join(sorted(unknown_arm_ids))
|
|
293
|
+
)
|
|
294
|
+
mixed = {item.profile_kind for item in arms}
|
|
295
|
+
if mixed != {self.estimand_profile_kind}:
|
|
296
|
+
raise ValidationError(
|
|
297
|
+
"one experiment manifest cannot mix official_native, controlled_adapter, "
|
|
298
|
+
"or factorial_adapter estimands"
|
|
299
|
+
)
|
|
300
|
+
if not seeds or any(
|
|
301
|
+
isinstance(seed, bool) or not isinstance(seed, int) or seed < 0 for seed in seeds
|
|
302
|
+
):
|
|
303
|
+
raise ValidationError("experiment seeds must be non-negative integers")
|
|
304
|
+
if len(seeds) != len(set(seeds)):
|
|
305
|
+
raise ValidationError("experiment seeds must be unique")
|
|
306
|
+
if (
|
|
307
|
+
not model_slots
|
|
308
|
+
or len(model_slots) != len(set(model_slots))
|
|
309
|
+
or any(
|
|
310
|
+
not isinstance(model_slot, str) or not model_slot.strip()
|
|
311
|
+
for model_slot in model_slots
|
|
312
|
+
)
|
|
313
|
+
):
|
|
314
|
+
raise ValidationError("experiment model_slots must be unique non-empty identifiers")
|
|
315
|
+
for model_slot in model_slots:
|
|
316
|
+
_identity(model_slot, label="experiment model_slot")
|
|
317
|
+
if self.driver_entrypoint is not None and not _ENTRYPOINT.fullmatch(self.driver_entrypoint):
|
|
318
|
+
raise ValidationError("driver_entrypoint must use module:attribute syntax")
|
|
319
|
+
if not all(isinstance(item, ExperimentPrerequisite) for item in prerequisites):
|
|
320
|
+
raise ValidationError("experiment prerequisites must be typed")
|
|
321
|
+
if len({(item.kind, item.name) for item in prerequisites}) != len(prerequisites):
|
|
322
|
+
raise ValidationError("experiment prerequisites must be unique")
|
|
323
|
+
try:
|
|
324
|
+
metadata = freeze_mapping(self.metadata)
|
|
325
|
+
except (TypeError, ValueError) as exc:
|
|
326
|
+
raise ValidationError("experiment metadata must be canonical JSON") from exc
|
|
327
|
+
object.__setattr__(self, "benchmarks", benchmarks)
|
|
328
|
+
object.__setattr__(self, "arms", arms)
|
|
329
|
+
object.__setattr__(self, "seeds", seeds)
|
|
330
|
+
object.__setattr__(self, "model_slots", model_slots)
|
|
331
|
+
object.__setattr__(self, "prerequisites", prerequisites)
|
|
332
|
+
object.__setattr__(self, "metadata", metadata)
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def manifest_hash(self) -> str:
|
|
336
|
+
return content_hash(self)
|
|
337
|
+
|
|
338
|
+
def to_dict(self) -> dict[str, Any]:
|
|
339
|
+
return to_jsonable(self)
|
|
340
|
+
|
|
341
|
+
@classmethod
|
|
342
|
+
def from_mapping(cls, value: Any) -> ExperimentManifest:
|
|
343
|
+
path = "manifest"
|
|
344
|
+
data = _strict_mapping(
|
|
345
|
+
value,
|
|
346
|
+
path=path,
|
|
347
|
+
keys={
|
|
348
|
+
"schema_version",
|
|
349
|
+
"study_id",
|
|
350
|
+
"stage",
|
|
351
|
+
"estimand_profile_kind",
|
|
352
|
+
"benchmarks",
|
|
353
|
+
"arms",
|
|
354
|
+
"seeds",
|
|
355
|
+
"model_slots",
|
|
356
|
+
"driver_entrypoint",
|
|
357
|
+
"prerequisites",
|
|
358
|
+
"metadata",
|
|
359
|
+
},
|
|
360
|
+
required={
|
|
361
|
+
"schema_version",
|
|
362
|
+
"study_id",
|
|
363
|
+
"stage",
|
|
364
|
+
"estimand_profile_kind",
|
|
365
|
+
"benchmarks",
|
|
366
|
+
"arms",
|
|
367
|
+
"seeds",
|
|
368
|
+
},
|
|
369
|
+
)
|
|
370
|
+
schema_version = data["schema_version"]
|
|
371
|
+
if isinstance(schema_version, bool) or not isinstance(schema_version, int):
|
|
372
|
+
raise ValidationError("manifest.schema_version must be an integer")
|
|
373
|
+
benchmarks = _sequence(data["benchmarks"], "manifest.benchmarks")
|
|
374
|
+
arms = _sequence(data["arms"], "manifest.arms")
|
|
375
|
+
seeds = _sequence(data["seeds"], "manifest.seeds")
|
|
376
|
+
model_slots = _sequence(
|
|
377
|
+
data.get("model_slots", ["default"]),
|
|
378
|
+
"manifest.model_slots",
|
|
379
|
+
)
|
|
380
|
+
prerequisites = _sequence(
|
|
381
|
+
data.get("prerequisites", []),
|
|
382
|
+
"manifest.prerequisites",
|
|
383
|
+
)
|
|
384
|
+
metadata = data.get("metadata", {})
|
|
385
|
+
if not isinstance(metadata, Mapping):
|
|
386
|
+
raise ValidationError("manifest.metadata must be an object")
|
|
387
|
+
return cls(
|
|
388
|
+
schema_version=schema_version,
|
|
389
|
+
study_id=_text(data["study_id"], "manifest.study_id"),
|
|
390
|
+
stage=_enum(ExperimentStage, data["stage"], "manifest.stage"),
|
|
391
|
+
estimand_profile_kind=_enum(
|
|
392
|
+
ProfileKind,
|
|
393
|
+
data["estimand_profile_kind"],
|
|
394
|
+
"manifest.estimand_profile_kind",
|
|
395
|
+
),
|
|
396
|
+
benchmarks=tuple(
|
|
397
|
+
ExperimentBenchmarkTarget.from_mapping(
|
|
398
|
+
item,
|
|
399
|
+
path=f"manifest.benchmarks[{index}]",
|
|
400
|
+
)
|
|
401
|
+
for index, item in enumerate(benchmarks)
|
|
402
|
+
),
|
|
403
|
+
arms=tuple(
|
|
404
|
+
ExperimentArmSpec.from_mapping(
|
|
405
|
+
item,
|
|
406
|
+
path=f"manifest.arms[{index}]",
|
|
407
|
+
)
|
|
408
|
+
for index, item in enumerate(arms)
|
|
409
|
+
),
|
|
410
|
+
seeds=tuple(
|
|
411
|
+
_integer(item, f"manifest.seeds[{index}]") for index, item in enumerate(seeds)
|
|
412
|
+
),
|
|
413
|
+
model_slots=tuple(
|
|
414
|
+
_text(item, f"manifest.model_slots[{index}]")
|
|
415
|
+
for index, item in enumerate(model_slots)
|
|
416
|
+
),
|
|
417
|
+
driver_entrypoint=_optional_text(
|
|
418
|
+
data.get("driver_entrypoint"),
|
|
419
|
+
"manifest.driver_entrypoint",
|
|
420
|
+
),
|
|
421
|
+
prerequisites=tuple(
|
|
422
|
+
ExperimentPrerequisite.from_mapping(
|
|
423
|
+
item,
|
|
424
|
+
path=f"manifest.prerequisites[{index}]",
|
|
425
|
+
)
|
|
426
|
+
for index, item in enumerate(prerequisites)
|
|
427
|
+
),
|
|
428
|
+
metadata=metadata,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _strict_mapping(
|
|
433
|
+
value: Any,
|
|
434
|
+
*,
|
|
435
|
+
path: str,
|
|
436
|
+
keys: set[str],
|
|
437
|
+
required: set[str],
|
|
438
|
+
) -> Mapping[str, Any]:
|
|
439
|
+
if not isinstance(value, Mapping):
|
|
440
|
+
raise ValidationError(f"{path} must be an object")
|
|
441
|
+
unknown = set(value) - keys
|
|
442
|
+
missing = required - set(value)
|
|
443
|
+
if unknown:
|
|
444
|
+
raise ValidationError(f"{path} contains unknown fields: {', '.join(sorted(unknown))}")
|
|
445
|
+
if missing:
|
|
446
|
+
raise ValidationError(f"{path} is missing fields: {', '.join(sorted(missing))}")
|
|
447
|
+
return value
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _sequence(value: Any, path: str) -> Sequence[Any]:
|
|
451
|
+
if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
|
|
452
|
+
raise ValidationError(f"{path} must be an array")
|
|
453
|
+
return value
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _string_tuple(value: Any, path: str) -> tuple[str, ...]:
|
|
457
|
+
return tuple(
|
|
458
|
+
_text(item, f"{path}[{index}]") for index, item in enumerate(_sequence(value, path))
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _text(value: Any, path: str) -> str:
|
|
463
|
+
if not isinstance(value, str) or not value.strip() or value != value.strip():
|
|
464
|
+
raise ValidationError(f"{path} must be a trimmed non-empty string")
|
|
465
|
+
return value
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _optional_text(value: Any, path: str) -> str | None:
|
|
469
|
+
return None if value is None else _text(value, path)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _integer(value: Any, path: str) -> int:
|
|
473
|
+
if isinstance(value, bool) or not isinstance(value, int):
|
|
474
|
+
raise ValidationError(f"{path} must be an integer")
|
|
475
|
+
return value
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def _boolean(value: Any, path: str) -> bool:
|
|
479
|
+
if not isinstance(value, bool):
|
|
480
|
+
raise ValidationError(f"{path} must be a boolean")
|
|
481
|
+
return value
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _enum(enum_type: type[Any], value: Any, path: str) -> Any:
|
|
485
|
+
if not isinstance(value, str):
|
|
486
|
+
raise ValidationError(f"{path} must be a string")
|
|
487
|
+
try:
|
|
488
|
+
return enum_type(value)
|
|
489
|
+
except ValueError as exc:
|
|
490
|
+
allowed = ", ".join(item.value for item in enum_type)
|
|
491
|
+
raise ValidationError(f"{path} must be one of: {allowed}") from exc
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _identity(value: str, *, label: str) -> None:
|
|
495
|
+
if not isinstance(value, str) or not _IDENTIFIER.fullmatch(value):
|
|
496
|
+
raise ValidationError(f"{label} contains unsupported characters")
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def require_sha256(value: str, *, label: str) -> str:
|
|
500
|
+
if not isinstance(value, str) or _SHA256.fullmatch(value) is None:
|
|
501
|
+
raise ValidationError(f"{label} must be a lowercase SHA-256")
|
|
502
|
+
return value
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
__all__ = [
|
|
506
|
+
"ExperimentArmKind",
|
|
507
|
+
"ExperimentArmSpec",
|
|
508
|
+
"ExperimentBenchmarkTarget",
|
|
509
|
+
"ExperimentManifest",
|
|
510
|
+
"ExperimentPrerequisite",
|
|
511
|
+
"ExperimentStage",
|
|
512
|
+
"PrerequisiteKind",
|
|
513
|
+
"require_sha256",
|
|
514
|
+
]
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Fail-closed secret screening for persisted experiment artifacts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Mapping, Sequence
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from agentinputlab.contracts.errors import ArtifactError
|
|
10
|
+
|
|
11
|
+
_SECRET_VALUE_PATTERNS = (
|
|
12
|
+
re.compile(r"(?i)\bbearer\s+[A-Za-z0-9._~+/=-]{8,}"),
|
|
13
|
+
re.compile(r"\bsk-[A-Za-z0-9_-]{8,}"),
|
|
14
|
+
re.compile(r"\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b"),
|
|
15
|
+
re.compile(
|
|
16
|
+
r"(?i)([?&](?:api[_-]?key|access[_-]?token|token|password|client[_-]?secret)"
|
|
17
|
+
r"=)[^&#\s]+"
|
|
18
|
+
),
|
|
19
|
+
re.compile(
|
|
20
|
+
r"(?i)(\b(?:api[_-]?key|x[_-]?api[_-]?key|authorization|access[_-]?token|"
|
|
21
|
+
r"refresh[_-]?token|password|passwd|client[_-]?secret|secret[_-]?key)"
|
|
22
|
+
r"\b\s*[:=]\s*)['\"]?(?:bearer\s+)?[^\s,;'\"]{4,}"
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
_SECRET_KEYS = frozenset(
|
|
26
|
+
{
|
|
27
|
+
"api_key",
|
|
28
|
+
"apikey",
|
|
29
|
+
"authorization",
|
|
30
|
+
"access_token",
|
|
31
|
+
"refresh_token",
|
|
32
|
+
"token",
|
|
33
|
+
"password",
|
|
34
|
+
"passwd",
|
|
35
|
+
"client_secret",
|
|
36
|
+
"secret",
|
|
37
|
+
"secret_key",
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
_SECRET_ENVIRONMENT_NAME = re.compile(
|
|
41
|
+
r"(?i)(?:^|_)(?:api_?key|authorization|credential|password|passwd|"
|
|
42
|
+
r"secret|token)(?:$|_)"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def redact_secret_text(
|
|
47
|
+
value: str,
|
|
48
|
+
*,
|
|
49
|
+
secret_values: Sequence[str] = (),
|
|
50
|
+
limit: int = 2048,
|
|
51
|
+
) -> str:
|
|
52
|
+
"""Redact known values and common credential forms before persistence.
|
|
53
|
+
|
|
54
|
+
Pattern matching is only a fallback: opaque credentials do not necessarily
|
|
55
|
+
have a recognizable prefix. Process drivers therefore pass the exact
|
|
56
|
+
values of their explicitly allowlisted environment variables here. Empty
|
|
57
|
+
values are ignored. Short values are retained by
|
|
58
|
+
:func:`environment_secret_values` only for secret-bearing variable names.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
result = value
|
|
62
|
+
known_values = {item for item in secret_values if isinstance(item, str) and item}
|
|
63
|
+
for secret in sorted(known_values, key=len, reverse=True):
|
|
64
|
+
result = result.replace(secret, "<redacted>")
|
|
65
|
+
for pattern in _SECRET_VALUE_PATTERNS:
|
|
66
|
+
result = pattern.sub(_redacted_match, result)
|
|
67
|
+
return result[:limit]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def environment_secret_values(
|
|
71
|
+
environment: Mapping[str, str],
|
|
72
|
+
names: Sequence[str],
|
|
73
|
+
) -> tuple[str, ...]:
|
|
74
|
+
"""Return opaque values for only the explicitly passed worker variables."""
|
|
75
|
+
|
|
76
|
+
return tuple(
|
|
77
|
+
dict.fromkeys(
|
|
78
|
+
value
|
|
79
|
+
for name in names
|
|
80
|
+
if isinstance(name, str)
|
|
81
|
+
and isinstance((value := environment.get(name)), str)
|
|
82
|
+
and value
|
|
83
|
+
and (len(value) >= 4 or _SECRET_ENVIRONMENT_NAME.search(name))
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def require_secret_free(value: Any, *, label: str) -> None:
|
|
89
|
+
"""Reject credentials before a fingerprint or result reaches disk."""
|
|
90
|
+
|
|
91
|
+
_walk(value, path=label)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def require_secret_values_absent(
|
|
95
|
+
value: Any,
|
|
96
|
+
*,
|
|
97
|
+
secret_values: Sequence[str],
|
|
98
|
+
label: str,
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Reject opaque passed-environment values before accepting worker output."""
|
|
101
|
+
|
|
102
|
+
known_values = tuple(
|
|
103
|
+
sorted(
|
|
104
|
+
{item for item in secret_values if isinstance(item, str) and item},
|
|
105
|
+
key=len,
|
|
106
|
+
reverse=True,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
if known_values:
|
|
110
|
+
_walk_known_values(
|
|
111
|
+
value,
|
|
112
|
+
path=label,
|
|
113
|
+
known_values=known_values,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _walk(value: Any, *, path: str) -> None:
|
|
118
|
+
if isinstance(value, Mapping):
|
|
119
|
+
for key, item in value.items():
|
|
120
|
+
if not isinstance(key, str):
|
|
121
|
+
continue
|
|
122
|
+
normalized = key.casefold().replace("-", "_")
|
|
123
|
+
if normalized in _SECRET_KEYS:
|
|
124
|
+
raise ArtifactError(f"{path} contains forbidden secret field {key!r}")
|
|
125
|
+
_walk(item, path=f"{path}.{key}")
|
|
126
|
+
return
|
|
127
|
+
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
|
|
128
|
+
for index, item in enumerate(value):
|
|
129
|
+
_walk(item, path=f"{path}[{index}]")
|
|
130
|
+
return
|
|
131
|
+
if isinstance(value, str) and redact_secret_text(value, limit=len(value)) != value:
|
|
132
|
+
raise ArtifactError(f"{path} contains credential-shaped text")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _walk_known_values(
|
|
136
|
+
value: Any,
|
|
137
|
+
*,
|
|
138
|
+
path: str,
|
|
139
|
+
known_values: tuple[str, ...],
|
|
140
|
+
) -> None:
|
|
141
|
+
if isinstance(value, Mapping):
|
|
142
|
+
for key, item in value.items():
|
|
143
|
+
if isinstance(key, str):
|
|
144
|
+
_walk_known_values(
|
|
145
|
+
key,
|
|
146
|
+
path=f"{path}.<key>",
|
|
147
|
+
known_values=known_values,
|
|
148
|
+
)
|
|
149
|
+
_walk_known_values(
|
|
150
|
+
item,
|
|
151
|
+
path=f"{path}.{key}",
|
|
152
|
+
known_values=known_values,
|
|
153
|
+
)
|
|
154
|
+
return
|
|
155
|
+
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
|
|
156
|
+
for index, item in enumerate(value):
|
|
157
|
+
_walk_known_values(
|
|
158
|
+
item,
|
|
159
|
+
path=f"{path}[{index}]",
|
|
160
|
+
known_values=known_values,
|
|
161
|
+
)
|
|
162
|
+
return
|
|
163
|
+
if isinstance(value, str) and any(secret in value for secret in known_values):
|
|
164
|
+
raise ArtifactError(f"{path} contains a value copied from the worker environment")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _redacted_match(match: re.Match[str]) -> str:
|
|
168
|
+
prefix = match.group(1) if match.lastindex else ""
|
|
169
|
+
return f"{prefix}<redacted>"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
__all__ = [
|
|
173
|
+
"environment_secret_values",
|
|
174
|
+
"redact_secret_text",
|
|
175
|
+
"require_secret_free",
|
|
176
|
+
"require_secret_values_absent",
|
|
177
|
+
]
|