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,1018 @@
|
|
|
1
|
+
"""Production builders for checksum-locked external atomic arena workers.
|
|
2
|
+
|
|
3
|
+
These builders keep τ³, STATE-Bench, ToolSandbox, and AppWorld semantics in
|
|
4
|
+
their pinned upstream runtimes. AgentInputLab sends one complete two-arm
|
|
5
|
+
request to a reviewed worker process; that worker must restore the protected
|
|
6
|
+
origin twice, run independent instances, invoke the official scorer, and
|
|
7
|
+
return the typed atomic receipt. The host does not emulate an official
|
|
8
|
+
environment or accept two independently scored single-arm callbacks.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import hashlib
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
from collections.abc import Mapping, Sequence
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from types import MappingProxyType
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from agentinputlab.benchmarks import (
|
|
24
|
+
ATOMIC_EXTERNAL_PAIR_CONTRACT,
|
|
25
|
+
AtomicReplayArmReceipt,
|
|
26
|
+
AtomicReplayArmResult,
|
|
27
|
+
AtomicReplayPairResult,
|
|
28
|
+
AtomicReplayReceipt,
|
|
29
|
+
AtomicReplayRequest,
|
|
30
|
+
)
|
|
31
|
+
from agentinputlab.contracts.enums import SemanticFamily
|
|
32
|
+
from agentinputlab.contracts.errors import (
|
|
33
|
+
ArtifactError,
|
|
34
|
+
ExternalDependencyError,
|
|
35
|
+
ValidationError,
|
|
36
|
+
)
|
|
37
|
+
from agentinputlab.contracts.serialization import (
|
|
38
|
+
canonical_json,
|
|
39
|
+
content_hash,
|
|
40
|
+
freeze_mapping,
|
|
41
|
+
to_jsonable,
|
|
42
|
+
)
|
|
43
|
+
from agentinputlab.contracts.state import (
|
|
44
|
+
ContextBlock,
|
|
45
|
+
ExecutionBudget,
|
|
46
|
+
FrozenAgentState,
|
|
47
|
+
Message,
|
|
48
|
+
)
|
|
49
|
+
from agentinputlab.experiments.drivers.external_arena import (
|
|
50
|
+
ExternalArenaCheckpoint,
|
|
51
|
+
ExternalArmPayload,
|
|
52
|
+
ExternalArmPayloadBuildRequest,
|
|
53
|
+
ExternalBridgeBuildRequest,
|
|
54
|
+
ExternalCheckpointBuildRequest,
|
|
55
|
+
ExternalUnitPreflightRequest,
|
|
56
|
+
)
|
|
57
|
+
from agentinputlab.experiments.drivers.parity_process import (
|
|
58
|
+
LockedProcessWorker,
|
|
59
|
+
attest_locked_process_worker,
|
|
60
|
+
execute_locked_process_worker,
|
|
61
|
+
load_locked_process_worker,
|
|
62
|
+
locked_process_worker_environment,
|
|
63
|
+
)
|
|
64
|
+
from agentinputlab.experiments.schema import ExperimentArmKind
|
|
65
|
+
from agentinputlab.experiments.secrets import (
|
|
66
|
+
environment_secret_values,
|
|
67
|
+
redact_secret_text,
|
|
68
|
+
require_secret_free,
|
|
69
|
+
require_secret_values_absent,
|
|
70
|
+
)
|
|
71
|
+
from agentinputlab.integrations.external_workers.supervisor import (
|
|
72
|
+
LocalSubprocessSupervisor,
|
|
73
|
+
SupervisedWorkerError,
|
|
74
|
+
WorkerSupervisor,
|
|
75
|
+
worker_supervisor_from_environment,
|
|
76
|
+
)
|
|
77
|
+
from agentinputlab.observability import ResourceLimits, ResourceReceipt, ResourceUsage
|
|
78
|
+
|
|
79
|
+
CHECKPOINT_INVENTORY_PROTOCOL = "agentinputlab.external-checkpoint-inventory.v1"
|
|
80
|
+
ATOMIC_PROCESS_BRIDGE_PROTOCOL = "agentinputlab.atomic-process-bridge.v1"
|
|
81
|
+
ATOMIC_PROCESS_REQUEST_PROTOCOL = "agentinputlab.atomic-process-request.v1"
|
|
82
|
+
ATOMIC_PROCESS_RESPONSE_PROTOCOL = "agentinputlab.atomic-process-response.v1"
|
|
83
|
+
RELEASE_PAYLOAD_PROTOCOL = "agentinputlab.external-release-payload.v1"
|
|
84
|
+
|
|
85
|
+
_SCHEMA_VERSION = 1
|
|
86
|
+
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
87
|
+
_ENVIRONMENT_NAME = re.compile(r"^[A-Z_][A-Z0-9_]*$")
|
|
88
|
+
_ANONYMIZED_GROUP = re.compile(r"^group_[0-9a-f]{64}$")
|
|
89
|
+
_MAX_INVENTORY_BYTES = 64 * 1024 * 1024
|
|
90
|
+
_CORE_FAMILIES = frozenset(
|
|
91
|
+
{
|
|
92
|
+
SemanticFamily.REFORMULATE,
|
|
93
|
+
SemanticFamily.DECOMPOSE,
|
|
94
|
+
SemanticFamily.ACQUIRE,
|
|
95
|
+
SemanticFamily.CLARIFY,
|
|
96
|
+
SemanticFamily.CURATE,
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
_PRIVILEGED_KEY_PARTS = frozenset(
|
|
100
|
+
{
|
|
101
|
+
"answer",
|
|
102
|
+
"criterion",
|
|
103
|
+
"database",
|
|
104
|
+
"evaluator",
|
|
105
|
+
"gold",
|
|
106
|
+
"grader",
|
|
107
|
+
"label",
|
|
108
|
+
"reference",
|
|
109
|
+
"required_document",
|
|
110
|
+
"requirement",
|
|
111
|
+
"reward",
|
|
112
|
+
"simulator_prompt",
|
|
113
|
+
"target_action",
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
_USAGE_FIELDS = frozenset(
|
|
117
|
+
{
|
|
118
|
+
"model_calls",
|
|
119
|
+
"retrieval_calls",
|
|
120
|
+
"tool_calls",
|
|
121
|
+
"embedding_calls",
|
|
122
|
+
"unpriced_model_calls",
|
|
123
|
+
"input_tokens",
|
|
124
|
+
"output_tokens",
|
|
125
|
+
"cpu_seconds",
|
|
126
|
+
"accelerator_seconds",
|
|
127
|
+
"cost_usd",
|
|
128
|
+
"compute_cost_usd",
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
_WORKER_FIELDS = frozenset(
|
|
132
|
+
{
|
|
133
|
+
"checkout_environment",
|
|
134
|
+
"expected_checkout_revision",
|
|
135
|
+
"expected_checkout_tree",
|
|
136
|
+
"adapter_checkout_environment",
|
|
137
|
+
"expected_adapter_checkout_revision",
|
|
138
|
+
"expected_adapter_checkout_tree",
|
|
139
|
+
"executable_path",
|
|
140
|
+
"executable_sha256",
|
|
141
|
+
"entrypoint",
|
|
142
|
+
"entrypoint_sha256",
|
|
143
|
+
"arguments",
|
|
144
|
+
"pass_environment",
|
|
145
|
+
"request_parameters",
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass(frozen=True, slots=True)
|
|
151
|
+
class _Inventory:
|
|
152
|
+
path: Path
|
|
153
|
+
file_sha256: str
|
|
154
|
+
checkpoints: Mapping[tuple[str, str, str], _CheckpointRecord]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass(frozen=True, slots=True)
|
|
158
|
+
class _CheckpointRecord:
|
|
159
|
+
benchmark_id: str
|
|
160
|
+
checkpoint_id: str
|
|
161
|
+
group_id: str
|
|
162
|
+
source_revision: str
|
|
163
|
+
origin_state_hash: str
|
|
164
|
+
replay_identity: str
|
|
165
|
+
dependencies_by_model_slot: Mapping[str, Mapping[str, str]]
|
|
166
|
+
visible_state: FrozenAgentState
|
|
167
|
+
eligible_families: tuple[SemanticFamily, ...]
|
|
168
|
+
|
|
169
|
+
def build(self, model_slot: str) -> ExternalArenaCheckpoint:
|
|
170
|
+
dependencies = self.dependencies_by_model_slot.get(
|
|
171
|
+
model_slot,
|
|
172
|
+
self.dependencies_by_model_slot.get("*"),
|
|
173
|
+
)
|
|
174
|
+
if dependencies is None:
|
|
175
|
+
raise ArtifactError(
|
|
176
|
+
f"checkpoint inventory has no dependency binding for model slot {model_slot!r}"
|
|
177
|
+
)
|
|
178
|
+
return ExternalArenaCheckpoint(
|
|
179
|
+
benchmark_id=self.benchmark_id,
|
|
180
|
+
checkpoint_id=self.checkpoint_id,
|
|
181
|
+
group_id=self.group_id,
|
|
182
|
+
source_revision=self.source_revision,
|
|
183
|
+
origin_state_hash=self.origin_state_hash,
|
|
184
|
+
replay_identity=self.replay_identity,
|
|
185
|
+
locked_dependency_hashes=dependencies,
|
|
186
|
+
visible_state=self.visible_state,
|
|
187
|
+
eligible_families=self.eligible_families,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class FrozenCheckpointInventoryBuilder:
|
|
192
|
+
"""Load evaluator-blind checkpoints from an exact-byte inventory."""
|
|
193
|
+
|
|
194
|
+
builder_kind = "arena_checkpoint"
|
|
195
|
+
|
|
196
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
197
|
+
return freeze_mapping(
|
|
198
|
+
{
|
|
199
|
+
"builder_id": CHECKPOINT_INVENTORY_PROTOCOL,
|
|
200
|
+
"schema_version": _SCHEMA_VERSION,
|
|
201
|
+
"gold_policy": "recursive_key_denylist_and_visible_state_only",
|
|
202
|
+
"group_policy": "privacy_safe_content_hash",
|
|
203
|
+
"inventory_digest_policy": "config_lock_plus_environment_recheck.v1",
|
|
204
|
+
"max_inventory_bytes": _MAX_INVENTORY_BYTES,
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def preflight(self, parameters: Mapping[str, Any]) -> Sequence[str]:
|
|
209
|
+
try:
|
|
210
|
+
_load_inventory(parameters)
|
|
211
|
+
except (ArtifactError, ValidationError) as exc:
|
|
212
|
+
return (redact_secret_text(str(exc)),)
|
|
213
|
+
return ()
|
|
214
|
+
|
|
215
|
+
def build(self, request: ExternalCheckpointBuildRequest) -> ExternalArenaCheckpoint:
|
|
216
|
+
if not isinstance(request, ExternalCheckpointBuildRequest):
|
|
217
|
+
raise TypeError("checkpoint inventory builder requires ExternalCheckpointBuildRequest")
|
|
218
|
+
inventory = _load_inventory(request.builder_parameters)
|
|
219
|
+
key = (request.benchmark_id, request.split, request.checkpoint_id)
|
|
220
|
+
try:
|
|
221
|
+
record = inventory.checkpoints[key]
|
|
222
|
+
except KeyError as exc:
|
|
223
|
+
raise ArtifactError(
|
|
224
|
+
"checkpoint inventory does not contain the requested benchmark/split/unit"
|
|
225
|
+
) from exc
|
|
226
|
+
checkpoint = record.build(request.model_slot)
|
|
227
|
+
if checkpoint.source_revision != request.source_revision:
|
|
228
|
+
raise ArtifactError("checkpoint source revision differs from the request")
|
|
229
|
+
return checkpoint
|
|
230
|
+
|
|
231
|
+
def preflight_units(
|
|
232
|
+
self,
|
|
233
|
+
request: ExternalUnitPreflightRequest,
|
|
234
|
+
) -> Sequence[str]:
|
|
235
|
+
if not isinstance(request, ExternalUnitPreflightRequest):
|
|
236
|
+
raise TypeError("unit preflight requires ExternalUnitPreflightRequest")
|
|
237
|
+
try:
|
|
238
|
+
inventory = _load_inventory(request.builder_parameters)
|
|
239
|
+
except (ArtifactError, ValidationError) as exc:
|
|
240
|
+
return (redact_secret_text(str(exc)),)
|
|
241
|
+
available = {
|
|
242
|
+
checkpoint_id
|
|
243
|
+
for benchmark_id, split, checkpoint_id in inventory.checkpoints
|
|
244
|
+
if benchmark_id == request.benchmark_id and split == request.split
|
|
245
|
+
}
|
|
246
|
+
requested = set(request.unit_ids)
|
|
247
|
+
missing = sorted(requested - available)
|
|
248
|
+
if missing:
|
|
249
|
+
return ("checkpoint inventory is missing resolved units: " + ", ".join(missing[:10]),)
|
|
250
|
+
revisions = {
|
|
251
|
+
record.source_revision
|
|
252
|
+
for key, record in inventory.checkpoints.items()
|
|
253
|
+
if key[0] == request.benchmark_id and key[1] == request.split and key[2] in requested
|
|
254
|
+
}
|
|
255
|
+
if revisions != {request.source_revision}:
|
|
256
|
+
return ("checkpoint inventory source revision differs from unit preflight",)
|
|
257
|
+
return ()
|
|
258
|
+
|
|
259
|
+
def preflight_group_ids(
|
|
260
|
+
self,
|
|
261
|
+
request: ExternalUnitPreflightRequest,
|
|
262
|
+
) -> Mapping[str, str]:
|
|
263
|
+
blockers = self.preflight_units(request)
|
|
264
|
+
if blockers:
|
|
265
|
+
raise ArtifactError("; ".join(blockers))
|
|
266
|
+
inventory = _load_inventory(request.builder_parameters)
|
|
267
|
+
return freeze_mapping(
|
|
268
|
+
{
|
|
269
|
+
checkpoint_id: inventory.checkpoints[
|
|
270
|
+
(request.benchmark_id, request.split, checkpoint_id)
|
|
271
|
+
].group_id
|
|
272
|
+
for checkpoint_id in request.unit_ids
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@dataclass(slots=True)
|
|
278
|
+
class _AtomicProcessBridge:
|
|
279
|
+
atomic_replay_contract: str
|
|
280
|
+
benchmark_id: str
|
|
281
|
+
source_revision: str
|
|
282
|
+
bridge_id: str
|
|
283
|
+
bridge_revision: str
|
|
284
|
+
scorer_id: str
|
|
285
|
+
scorer_revision: str
|
|
286
|
+
locked_dependency_hashes: Mapping[str, str]
|
|
287
|
+
worker: LockedProcessWorker
|
|
288
|
+
worker_state: Mapping[str, Any]
|
|
289
|
+
supervisor: WorkerSupervisor
|
|
290
|
+
resource_limits: ResourceLimits
|
|
291
|
+
pipe_limits: Mapping[str, int]
|
|
292
|
+
environment: Mapping[str, str] = field(repr=False, compare=False)
|
|
293
|
+
config_identity: str
|
|
294
|
+
last_resource_receipt: ResourceReceipt | None = None
|
|
295
|
+
|
|
296
|
+
def __post_init__(self) -> None:
|
|
297
|
+
object.__setattr__(
|
|
298
|
+
self,
|
|
299
|
+
"locked_dependency_hashes",
|
|
300
|
+
freeze_mapping(self.locked_dependency_hashes),
|
|
301
|
+
)
|
|
302
|
+
object.__setattr__(self, "worker_state", freeze_mapping(self.worker_state))
|
|
303
|
+
object.__setattr__(self, "pipe_limits", freeze_mapping(self.pipe_limits))
|
|
304
|
+
object.__setattr__(self, "environment", freeze_mapping(self.environment))
|
|
305
|
+
|
|
306
|
+
def evaluate_pair(self, request: AtomicReplayRequest) -> AtomicReplayPairResult:
|
|
307
|
+
if not isinstance(request, AtomicReplayRequest):
|
|
308
|
+
raise TypeError("atomic process bridge requires AtomicReplayRequest")
|
|
309
|
+
if (
|
|
310
|
+
request.benchmark_id != self.benchmark_id
|
|
311
|
+
or request.source_revision != self.source_revision
|
|
312
|
+
or request.locked_dependency_hashes != self.locked_dependency_hashes
|
|
313
|
+
):
|
|
314
|
+
raise ValidationError(
|
|
315
|
+
"atomic process request differs from the constructed bridge identity"
|
|
316
|
+
)
|
|
317
|
+
base = {
|
|
318
|
+
"schema_version": _SCHEMA_VERSION,
|
|
319
|
+
"protocol": ATOMIC_PROCESS_REQUEST_PROTOCOL,
|
|
320
|
+
"atomic_request": to_jsonable(request),
|
|
321
|
+
"worker_parameters": self.worker.request_parameters,
|
|
322
|
+
}
|
|
323
|
+
require_secret_free(base, label="atomic process request")
|
|
324
|
+
payload = {
|
|
325
|
+
**base,
|
|
326
|
+
"request_hash": content_hash(base),
|
|
327
|
+
}
|
|
328
|
+
environment = locked_process_worker_environment(
|
|
329
|
+
self.worker,
|
|
330
|
+
environment=self.environment,
|
|
331
|
+
pass_environment=self.worker.pass_environment,
|
|
332
|
+
request_protocol=ATOMIC_PROCESS_REQUEST_PROTOCOL,
|
|
333
|
+
)
|
|
334
|
+
try:
|
|
335
|
+
process = execute_locked_process_worker(
|
|
336
|
+
self.worker,
|
|
337
|
+
supervisor=self.supervisor,
|
|
338
|
+
input_text=canonical_json(payload) + "\n",
|
|
339
|
+
limits=self.resource_limits,
|
|
340
|
+
pipe_limits=self.pipe_limits,
|
|
341
|
+
environment=environment,
|
|
342
|
+
process_name=f"atomic-arena-{self.benchmark_id}",
|
|
343
|
+
config_sha256=self.config_identity,
|
|
344
|
+
expected_worker_state=self.worker_state,
|
|
345
|
+
)
|
|
346
|
+
except SupervisedWorkerError as exc:
|
|
347
|
+
object.__setattr__(self, "last_resource_receipt", exc.resource_receipt)
|
|
348
|
+
raise
|
|
349
|
+
object.__setattr__(self, "last_resource_receipt", process.resource_receipt)
|
|
350
|
+
if process.returncode != 0:
|
|
351
|
+
raise SupervisedWorkerError(
|
|
352
|
+
"atomic arena worker failed: "
|
|
353
|
+
+ redact_secret_text(
|
|
354
|
+
process.stderr.strip() or "non-zero exit",
|
|
355
|
+
secret_values=environment_secret_values(
|
|
356
|
+
self.environment,
|
|
357
|
+
self.worker.pass_environment,
|
|
358
|
+
),
|
|
359
|
+
),
|
|
360
|
+
resource_receipt=process.resource_receipt,
|
|
361
|
+
)
|
|
362
|
+
result = _parse_atomic_response(
|
|
363
|
+
process.stdout,
|
|
364
|
+
expected_request_hash=payload["request_hash"],
|
|
365
|
+
secret_values=environment_secret_values(
|
|
366
|
+
self.environment,
|
|
367
|
+
self.worker.pass_environment,
|
|
368
|
+
),
|
|
369
|
+
)
|
|
370
|
+
return AtomicReplayPairResult(
|
|
371
|
+
receipt=result.receipt,
|
|
372
|
+
arms=result.arms,
|
|
373
|
+
resource_receipts=(process.resource_receipt,),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
@dataclass(frozen=True, slots=True)
|
|
378
|
+
class _ParsedAtomicResponse:
|
|
379
|
+
receipt: AtomicReplayReceipt
|
|
380
|
+
arms: tuple[AtomicReplayArmResult, ...]
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class AtomicProcessBridgeBuilder:
|
|
384
|
+
"""Construct an atomic bridge around one exact-checkout worker process."""
|
|
385
|
+
|
|
386
|
+
builder_kind = "atomic_bridge"
|
|
387
|
+
|
|
388
|
+
def __init__(self, supervisor: WorkerSupervisor | None = None) -> None:
|
|
389
|
+
self._supervisor = supervisor or LocalSubprocessSupervisor()
|
|
390
|
+
if not isinstance(self._supervisor, WorkerSupervisor):
|
|
391
|
+
raise TypeError("atomic process builder supervisor must implement WorkerSupervisor")
|
|
392
|
+
|
|
393
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
394
|
+
return freeze_mapping(
|
|
395
|
+
{
|
|
396
|
+
"builder_id": ATOMIC_PROCESS_BRIDGE_PROTOCOL,
|
|
397
|
+
"schema_version": _SCHEMA_VERSION,
|
|
398
|
+
"request_protocol": ATOMIC_PROCESS_REQUEST_PROTOCOL,
|
|
399
|
+
"response_protocol": ATOMIC_PROCESS_RESPONSE_PROTOCOL,
|
|
400
|
+
"worker_identity": "clean_git_checkout_plus_entrypoint_sha256",
|
|
401
|
+
"supervisor": self._supervisor.fingerprint(),
|
|
402
|
+
"atomicity": "one_process_call_per_baseline_treatment_pair",
|
|
403
|
+
}
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
def preflight(self, parameters: Mapping[str, Any]) -> Sequence[str]:
|
|
407
|
+
try:
|
|
408
|
+
parsed = _parse_bridge_parameters(parameters)
|
|
409
|
+
worker = _load_atomic_worker(parsed, environment=os.environ)
|
|
410
|
+
attest_locked_process_worker(worker)
|
|
411
|
+
if isinstance(self._supervisor, LocalSubprocessSupervisor) and any(
|
|
412
|
+
value is not None
|
|
413
|
+
for value in (
|
|
414
|
+
parsed["resource_limits"].memory_bytes,
|
|
415
|
+
parsed["resource_limits"].cpus,
|
|
416
|
+
parsed["resource_limits"].pids_limit,
|
|
417
|
+
)
|
|
418
|
+
):
|
|
419
|
+
raise ValidationError(
|
|
420
|
+
"local atomic process supervision supports wall/pipe limits only"
|
|
421
|
+
)
|
|
422
|
+
missing = [name for name in worker.pass_environment if not os.environ.get(name)]
|
|
423
|
+
if missing:
|
|
424
|
+
raise ArtifactError(
|
|
425
|
+
"atomic process worker is missing environments: " + ", ".join(sorted(missing))
|
|
426
|
+
)
|
|
427
|
+
except (
|
|
428
|
+
ArtifactError,
|
|
429
|
+
ExternalDependencyError,
|
|
430
|
+
ValidationError,
|
|
431
|
+
) as exc:
|
|
432
|
+
return (redact_secret_text(str(exc)),)
|
|
433
|
+
return ()
|
|
434
|
+
|
|
435
|
+
def build(self, request: ExternalBridgeBuildRequest) -> _AtomicProcessBridge:
|
|
436
|
+
if not isinstance(request, ExternalBridgeBuildRequest):
|
|
437
|
+
raise TypeError("atomic process builder requires ExternalBridgeBuildRequest")
|
|
438
|
+
parsed = _parse_bridge_parameters(request.builder_parameters)
|
|
439
|
+
checkpoint = request.checkpoint
|
|
440
|
+
if (
|
|
441
|
+
parsed["benchmark_id"] != checkpoint.benchmark_id
|
|
442
|
+
or parsed["source_revision"] != checkpoint.source_revision
|
|
443
|
+
):
|
|
444
|
+
raise ValidationError("atomic process builder identity differs from its checkpoint")
|
|
445
|
+
worker = _load_atomic_worker(parsed, environment=os.environ)
|
|
446
|
+
state = attest_locked_process_worker(worker)
|
|
447
|
+
missing = [name for name in worker.pass_environment if not os.environ.get(name)]
|
|
448
|
+
if missing:
|
|
449
|
+
raise ArtifactError(
|
|
450
|
+
"atomic process worker is missing environments: " + ", ".join(sorted(missing))
|
|
451
|
+
)
|
|
452
|
+
worker_environment = {name: os.environ[name] for name in worker.pass_environment}
|
|
453
|
+
return _AtomicProcessBridge(
|
|
454
|
+
atomic_replay_contract=ATOMIC_EXTERNAL_PAIR_CONTRACT,
|
|
455
|
+
benchmark_id=parsed["benchmark_id"],
|
|
456
|
+
source_revision=parsed["source_revision"],
|
|
457
|
+
bridge_id=parsed["bridge_id"],
|
|
458
|
+
bridge_revision=parsed["bridge_revision"],
|
|
459
|
+
scorer_id=parsed["scorer_id"],
|
|
460
|
+
scorer_revision=parsed["scorer_revision"],
|
|
461
|
+
locked_dependency_hashes=checkpoint.locked_dependency_hashes,
|
|
462
|
+
worker=worker,
|
|
463
|
+
worker_state=state,
|
|
464
|
+
supervisor=self._supervisor,
|
|
465
|
+
resource_limits=parsed["resource_limits"],
|
|
466
|
+
pipe_limits=parsed["pipe_limits"],
|
|
467
|
+
environment=worker_environment,
|
|
468
|
+
config_identity=content_hash(request.builder_parameters),
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
class ReleaseArmPayloadBuilder:
|
|
473
|
+
"""Serialize a release arm without interpreting or rewriting its algorithm."""
|
|
474
|
+
|
|
475
|
+
builder_kind = "arm_payload"
|
|
476
|
+
|
|
477
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
478
|
+
return freeze_mapping(
|
|
479
|
+
{
|
|
480
|
+
"builder_id": RELEASE_PAYLOAD_PROTOCOL,
|
|
481
|
+
"schema_version": _SCHEMA_VERSION,
|
|
482
|
+
"payload_semantics": "identity_plus_frozen_settings",
|
|
483
|
+
"policy_arms": "unsupported_use_selector_specific_builder",
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
def preflight(self, parameters: Mapping[str, Any]) -> Sequence[str]:
|
|
488
|
+
try:
|
|
489
|
+
_parse_payload_parameters(parameters)
|
|
490
|
+
except (ArtifactError, ValidationError) as exc:
|
|
491
|
+
return (redact_secret_text(str(exc)),)
|
|
492
|
+
return ()
|
|
493
|
+
|
|
494
|
+
def build(self, request: ExternalArmPayloadBuildRequest) -> ExternalArmPayload:
|
|
495
|
+
if not isinstance(request, ExternalArmPayloadBuildRequest):
|
|
496
|
+
raise TypeError("release payload builder requires ExternalArmPayloadBuildRequest")
|
|
497
|
+
if request.arm_kind is not ExperimentArmKind.RELEASE:
|
|
498
|
+
raise ValidationError("release payload builder cannot construct selector policy arms")
|
|
499
|
+
parameters = _parse_payload_parameters(request.builder_parameters)
|
|
500
|
+
allowed = parameters["allowed_release_ids"]
|
|
501
|
+
if request.release_id not in allowed:
|
|
502
|
+
raise ValidationError("release payload is outside the builder allowlist")
|
|
503
|
+
if request.release_id == "baseline.no_op" and request.family is not SemanticFamily.NO_OP:
|
|
504
|
+
raise ValidationError("baseline.no_op payload requires the no_op family")
|
|
505
|
+
if request.release_id != "baseline.no_op" and request.family not in _CORE_FAMILIES:
|
|
506
|
+
raise ValidationError("release payload requires a core intervention family")
|
|
507
|
+
payload = {
|
|
508
|
+
"schema_version": _SCHEMA_VERSION,
|
|
509
|
+
"protocol": RELEASE_PAYLOAD_PROTOCOL,
|
|
510
|
+
"arm_id": request.arm_id,
|
|
511
|
+
"release_id": request.release_id,
|
|
512
|
+
"family": request.family.value,
|
|
513
|
+
"profile_kind": request.profile_kind.value,
|
|
514
|
+
"model_slot": request.model_slot,
|
|
515
|
+
"seed": request.seed,
|
|
516
|
+
"checkpoint_identity_hash": request.checkpoint.identity_hash,
|
|
517
|
+
"visible_state_hash": request.checkpoint.visible_state.state_hash,
|
|
518
|
+
"pre_action": request.pre_action,
|
|
519
|
+
"release_parameters": parameters["release_parameters"],
|
|
520
|
+
"arm_parameters": request.arm_parameters,
|
|
521
|
+
}
|
|
522
|
+
require_secret_free(payload, label="external release payload")
|
|
523
|
+
return ExternalArmPayload(
|
|
524
|
+
arm_id=request.arm_id,
|
|
525
|
+
release_id=request.release_id,
|
|
526
|
+
family=request.family,
|
|
527
|
+
profile_kind=request.profile_kind,
|
|
528
|
+
payload=payload,
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def frozen_checkpoint_inventory_builder_factory() -> FrozenCheckpointInventoryBuilder:
|
|
533
|
+
return FrozenCheckpointInventoryBuilder()
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def atomic_process_bridge_builder_factory() -> AtomicProcessBridgeBuilder:
|
|
537
|
+
return AtomicProcessBridgeBuilder(worker_supervisor_from_environment())
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def release_arm_payload_builder_factory() -> ReleaseArmPayloadBuilder:
|
|
541
|
+
return ReleaseArmPayloadBuilder()
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def _load_inventory(parameters: Mapping[str, Any]) -> _Inventory:
|
|
545
|
+
parsed = _parse_inventory_parameters(parameters)
|
|
546
|
+
path_value = os.environ.get(parsed["path_environment"])
|
|
547
|
+
environment_digest = os.environ.get(parsed["sha256_environment"])
|
|
548
|
+
expected = parsed["expected_sha256"]
|
|
549
|
+
if not path_value:
|
|
550
|
+
raise ArtifactError(
|
|
551
|
+
f"missing checkpoint inventory environment {parsed['path_environment']}"
|
|
552
|
+
)
|
|
553
|
+
if not environment_digest or _SHA256.fullmatch(environment_digest) is None:
|
|
554
|
+
raise ArtifactError(
|
|
555
|
+
f"missing or invalid inventory digest environment {parsed['sha256_environment']}"
|
|
556
|
+
)
|
|
557
|
+
if environment_digest != expected:
|
|
558
|
+
raise ArtifactError("checkpoint inventory digest environment differs from the config lock")
|
|
559
|
+
unresolved = Path(path_value).expanduser()
|
|
560
|
+
if unresolved.is_symlink():
|
|
561
|
+
raise ArtifactError("checkpoint inventory must not be a symlink")
|
|
562
|
+
path = unresolved.resolve()
|
|
563
|
+
if not path.is_file() or path.stat().st_size > _MAX_INVENTORY_BYTES:
|
|
564
|
+
raise ArtifactError("checkpoint inventory is absent or exceeds the size limit")
|
|
565
|
+
raw_bytes = path.read_bytes()
|
|
566
|
+
observed = hashlib.sha256(raw_bytes).hexdigest()
|
|
567
|
+
if observed != expected:
|
|
568
|
+
raise ArtifactError("checkpoint inventory checksum mismatch")
|
|
569
|
+
try:
|
|
570
|
+
raw = json.loads(raw_bytes)
|
|
571
|
+
except (UnicodeError, json.JSONDecodeError) as exc:
|
|
572
|
+
raise ArtifactError("checkpoint inventory is not valid UTF-8 JSON") from exc
|
|
573
|
+
if (
|
|
574
|
+
not isinstance(raw, Mapping)
|
|
575
|
+
or set(raw) != {"schema_version", "protocol", "checkpoint_count", "checkpoints"}
|
|
576
|
+
or raw["schema_version"] != _SCHEMA_VERSION
|
|
577
|
+
or raw["protocol"] != CHECKPOINT_INVENTORY_PROTOCOL
|
|
578
|
+
or not isinstance(raw["checkpoints"], list)
|
|
579
|
+
or raw["checkpoint_count"] != len(raw["checkpoints"])
|
|
580
|
+
or not raw["checkpoints"]
|
|
581
|
+
):
|
|
582
|
+
raise ValidationError("checkpoint inventory schema is invalid")
|
|
583
|
+
checkpoints: dict[tuple[str, str, str], _CheckpointRecord] = {}
|
|
584
|
+
for index, item in enumerate(raw["checkpoints"]):
|
|
585
|
+
key, record = _checkpoint_from_mapping(
|
|
586
|
+
item,
|
|
587
|
+
label=f"checkpoints[{index}]",
|
|
588
|
+
)
|
|
589
|
+
if key in checkpoints:
|
|
590
|
+
raise ValidationError("checkpoint inventory contains duplicate units")
|
|
591
|
+
checkpoints[key] = record
|
|
592
|
+
return _Inventory(
|
|
593
|
+
path=path,
|
|
594
|
+
file_sha256=observed,
|
|
595
|
+
checkpoints=MappingProxyType(checkpoints),
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def _parse_inventory_parameters(parameters: Mapping[str, Any]) -> Mapping[str, str]:
|
|
600
|
+
if not isinstance(parameters, Mapping) or set(parameters) != {
|
|
601
|
+
"schema_version",
|
|
602
|
+
"protocol",
|
|
603
|
+
"path_environment",
|
|
604
|
+
"sha256_environment",
|
|
605
|
+
"expected_sha256",
|
|
606
|
+
}:
|
|
607
|
+
raise ValidationError("checkpoint inventory builder parameters are invalid")
|
|
608
|
+
if (
|
|
609
|
+
parameters["schema_version"] != _SCHEMA_VERSION
|
|
610
|
+
or parameters["protocol"] != CHECKPOINT_INVENTORY_PROTOCOL
|
|
611
|
+
):
|
|
612
|
+
raise ValidationError("unsupported checkpoint inventory builder protocol")
|
|
613
|
+
for key in ("path_environment", "sha256_environment"):
|
|
614
|
+
if (
|
|
615
|
+
not isinstance(parameters[key], str)
|
|
616
|
+
or _ENVIRONMENT_NAME.fullmatch(parameters[key]) is None
|
|
617
|
+
):
|
|
618
|
+
raise ValidationError(f"checkpoint inventory {key} is invalid")
|
|
619
|
+
if parameters["path_environment"] == parameters["sha256_environment"]:
|
|
620
|
+
raise ValidationError("checkpoint inventory path and digest environments must differ")
|
|
621
|
+
if (
|
|
622
|
+
not isinstance(parameters["expected_sha256"], str)
|
|
623
|
+
or _SHA256.fullmatch(parameters["expected_sha256"]) is None
|
|
624
|
+
):
|
|
625
|
+
raise ValidationError("checkpoint inventory expected_sha256 must be a lowercase SHA-256")
|
|
626
|
+
return freeze_mapping(parameters)
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def _checkpoint_from_mapping(
|
|
630
|
+
value: Any,
|
|
631
|
+
*,
|
|
632
|
+
label: str,
|
|
633
|
+
) -> tuple[tuple[str, str, str], _CheckpointRecord]:
|
|
634
|
+
common = {
|
|
635
|
+
"benchmark_id",
|
|
636
|
+
"split",
|
|
637
|
+
"checkpoint_id",
|
|
638
|
+
"group_id",
|
|
639
|
+
"source_revision",
|
|
640
|
+
"origin_state_hash",
|
|
641
|
+
"replay_identity",
|
|
642
|
+
"visible_state",
|
|
643
|
+
"visible_state_hash",
|
|
644
|
+
"eligible_families",
|
|
645
|
+
}
|
|
646
|
+
legacy = common | {"locked_dependency_hashes"}
|
|
647
|
+
model_aware = common | {"locked_dependency_hashes_by_model_slot"}
|
|
648
|
+
if not isinstance(value, Mapping) or (set(value) != legacy and set(value) != model_aware):
|
|
649
|
+
raise ValidationError(f"{label} fields are invalid")
|
|
650
|
+
for name in ("benchmark_id", "split", "checkpoint_id", "source_revision"):
|
|
651
|
+
_text(value[name], label=f"{label}.{name}")
|
|
652
|
+
if (
|
|
653
|
+
not isinstance(value["group_id"], str)
|
|
654
|
+
or _ANONYMIZED_GROUP.fullmatch(value["group_id"]) is None
|
|
655
|
+
):
|
|
656
|
+
raise ValidationError(f"{label}.group_id must be a privacy-safe group hash")
|
|
657
|
+
_reject_privileged_keys(value["visible_state"], path=f"{label}.visible_state")
|
|
658
|
+
state = _state_from_mapping(value["visible_state"], label=f"{label}.visible_state")
|
|
659
|
+
if value["visible_state_hash"] != state.state_hash:
|
|
660
|
+
raise ValidationError(f"{label}.visible_state_hash differs from the state")
|
|
661
|
+
raw_families = value["eligible_families"]
|
|
662
|
+
if isinstance(raw_families, (str, bytes)) or not isinstance(raw_families, Sequence):
|
|
663
|
+
raise ValidationError(f"{label}.eligible_families must be an array")
|
|
664
|
+
try:
|
|
665
|
+
families = tuple(SemanticFamily(item) for item in raw_families)
|
|
666
|
+
except (TypeError, ValueError) as exc:
|
|
667
|
+
raise ValidationError(f"{label}.eligible_families contains an unknown family") from exc
|
|
668
|
+
raw_dependencies = (
|
|
669
|
+
{"*": value["locked_dependency_hashes"]}
|
|
670
|
+
if "locked_dependency_hashes" in value
|
|
671
|
+
else value["locked_dependency_hashes_by_model_slot"]
|
|
672
|
+
)
|
|
673
|
+
if (
|
|
674
|
+
not isinstance(raw_dependencies, Mapping)
|
|
675
|
+
or not raw_dependencies
|
|
676
|
+
or any(
|
|
677
|
+
not isinstance(model_slot, str)
|
|
678
|
+
or not model_slot.strip()
|
|
679
|
+
or not isinstance(dependencies, Mapping)
|
|
680
|
+
for model_slot, dependencies in raw_dependencies.items()
|
|
681
|
+
)
|
|
682
|
+
):
|
|
683
|
+
raise ValidationError(f"{label}.locked_dependency_hashes_by_model_slot is invalid")
|
|
684
|
+
record = _CheckpointRecord(
|
|
685
|
+
benchmark_id=value["benchmark_id"],
|
|
686
|
+
checkpoint_id=value["checkpoint_id"],
|
|
687
|
+
group_id=value["group_id"],
|
|
688
|
+
source_revision=value["source_revision"],
|
|
689
|
+
origin_state_hash=value["origin_state_hash"],
|
|
690
|
+
replay_identity=value["replay_identity"],
|
|
691
|
+
dependencies_by_model_slot=freeze_mapping(raw_dependencies),
|
|
692
|
+
visible_state=state,
|
|
693
|
+
eligible_families=families,
|
|
694
|
+
)
|
|
695
|
+
for model_slot in raw_dependencies:
|
|
696
|
+
record.build(model_slot)
|
|
697
|
+
return (
|
|
698
|
+
(value["benchmark_id"], value["split"], value["checkpoint_id"]),
|
|
699
|
+
record,
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def _state_from_mapping(value: Any, *, label: str) -> FrozenAgentState:
|
|
704
|
+
fields = {
|
|
705
|
+
"query",
|
|
706
|
+
"task_id",
|
|
707
|
+
"instruction",
|
|
708
|
+
"context",
|
|
709
|
+
"messages",
|
|
710
|
+
"tool_names",
|
|
711
|
+
"metadata",
|
|
712
|
+
"budget",
|
|
713
|
+
}
|
|
714
|
+
if not isinstance(value, Mapping) or set(value) != fields:
|
|
715
|
+
raise ValidationError(f"{label} fields are invalid")
|
|
716
|
+
context_raw = value["context"]
|
|
717
|
+
messages_raw = value["messages"]
|
|
718
|
+
tools_raw = value["tool_names"]
|
|
719
|
+
if (
|
|
720
|
+
isinstance(context_raw, (str, bytes))
|
|
721
|
+
or not isinstance(context_raw, Sequence)
|
|
722
|
+
or isinstance(messages_raw, (str, bytes))
|
|
723
|
+
or not isinstance(messages_raw, Sequence)
|
|
724
|
+
or isinstance(tools_raw, (str, bytes))
|
|
725
|
+
or not isinstance(tools_raw, Sequence)
|
|
726
|
+
):
|
|
727
|
+
raise ValidationError(f"{label} sequence fields are invalid")
|
|
728
|
+
context = tuple(
|
|
729
|
+
_context_block(item, label=f"{label}.context[{index}]")
|
|
730
|
+
for index, item in enumerate(context_raw)
|
|
731
|
+
)
|
|
732
|
+
messages = tuple(
|
|
733
|
+
_message(item, label=f"{label}.messages[{index}]")
|
|
734
|
+
for index, item in enumerate(messages_raw)
|
|
735
|
+
)
|
|
736
|
+
budget_raw = value["budget"]
|
|
737
|
+
budget_fields = {
|
|
738
|
+
"max_model_calls",
|
|
739
|
+
"max_retrieval_calls",
|
|
740
|
+
"max_tool_calls",
|
|
741
|
+
"max_output_tokens",
|
|
742
|
+
"max_context_tokens",
|
|
743
|
+
"max_cost_usd",
|
|
744
|
+
}
|
|
745
|
+
if not isinstance(budget_raw, Mapping) or set(budget_raw) != budget_fields:
|
|
746
|
+
raise ValidationError(f"{label}.budget fields are invalid")
|
|
747
|
+
if not isinstance(value["metadata"], Mapping):
|
|
748
|
+
raise ValidationError(f"{label}.metadata must be an object")
|
|
749
|
+
return FrozenAgentState(
|
|
750
|
+
query=value["query"],
|
|
751
|
+
task_id=value["task_id"],
|
|
752
|
+
instruction=value["instruction"],
|
|
753
|
+
context=context,
|
|
754
|
+
messages=messages,
|
|
755
|
+
tool_names=tuple(tools_raw),
|
|
756
|
+
metadata=value["metadata"],
|
|
757
|
+
budget=ExecutionBudget(**budget_raw),
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def _context_block(value: Any, *, label: str) -> ContextBlock:
|
|
762
|
+
fields = {"text", "block_id", "source", "score", "metadata"}
|
|
763
|
+
if not isinstance(value, Mapping) or set(value) != fields:
|
|
764
|
+
raise ValidationError(f"{label} fields are invalid")
|
|
765
|
+
if not isinstance(value["metadata"], Mapping):
|
|
766
|
+
raise ValidationError(f"{label}.metadata must be an object")
|
|
767
|
+
return ContextBlock(**value)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def _message(value: Any, *, label: str) -> Message:
|
|
771
|
+
if not isinstance(value, Mapping) or set(value) != {"role", "content", "name"}:
|
|
772
|
+
raise ValidationError(f"{label} fields are invalid")
|
|
773
|
+
return Message(**value)
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
def _reject_privileged_keys(value: Any, *, path: str) -> None:
|
|
777
|
+
if isinstance(value, Mapping):
|
|
778
|
+
for key, item in value.items():
|
|
779
|
+
normalized = str(key).casefold().replace("-", "_")
|
|
780
|
+
if any(part in normalized for part in _PRIVILEGED_KEY_PARTS):
|
|
781
|
+
raise ValidationError(f"{path} contains privileged field {key!r}")
|
|
782
|
+
_reject_privileged_keys(item, path=f"{path}.{key}")
|
|
783
|
+
elif isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
|
|
784
|
+
for index, item in enumerate(value):
|
|
785
|
+
_reject_privileged_keys(item, path=f"{path}[{index}]")
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def _parse_bridge_parameters(parameters: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
789
|
+
fields = {
|
|
790
|
+
"schema_version",
|
|
791
|
+
"protocol",
|
|
792
|
+
"benchmark_id",
|
|
793
|
+
"source_revision",
|
|
794
|
+
"bridge_id",
|
|
795
|
+
"bridge_revision",
|
|
796
|
+
"scorer_id",
|
|
797
|
+
"scorer_revision",
|
|
798
|
+
"worker",
|
|
799
|
+
"resource_limits",
|
|
800
|
+
"pipe_limits",
|
|
801
|
+
}
|
|
802
|
+
if not isinstance(parameters, Mapping) or set(parameters) != fields:
|
|
803
|
+
raise ValidationError("atomic process bridge parameters are invalid")
|
|
804
|
+
if (
|
|
805
|
+
parameters["schema_version"] != _SCHEMA_VERSION
|
|
806
|
+
or parameters["protocol"] != ATOMIC_PROCESS_BRIDGE_PROTOCOL
|
|
807
|
+
):
|
|
808
|
+
raise ValidationError("unsupported atomic process bridge protocol")
|
|
809
|
+
for name in ("benchmark_id", "source_revision", "bridge_id", "scorer_id"):
|
|
810
|
+
_text(parameters[name], label=f"atomic process {name}")
|
|
811
|
+
for name in ("bridge_revision", "scorer_revision"):
|
|
812
|
+
_sha256(parameters[name], label=f"atomic process {name}")
|
|
813
|
+
worker = parameters["worker"]
|
|
814
|
+
if not isinstance(worker, Mapping) or set(worker) != _WORKER_FIELDS:
|
|
815
|
+
raise ValidationError("atomic process worker fields are invalid")
|
|
816
|
+
limits = parameters["resource_limits"]
|
|
817
|
+
if not isinstance(limits, Mapping) or set(limits) != {
|
|
818
|
+
"wall_timeout_seconds",
|
|
819
|
+
"memory_bytes",
|
|
820
|
+
"cpus",
|
|
821
|
+
"pids_limit",
|
|
822
|
+
}:
|
|
823
|
+
raise ValidationError("atomic process resource_limits fields are invalid")
|
|
824
|
+
pipes = parameters["pipe_limits"]
|
|
825
|
+
if not isinstance(pipes, Mapping) or set(pipes) != {
|
|
826
|
+
"max_input_bytes",
|
|
827
|
+
"max_stdout_bytes",
|
|
828
|
+
"max_stderr_bytes",
|
|
829
|
+
}:
|
|
830
|
+
raise ValidationError("atomic process pipe_limits fields are invalid")
|
|
831
|
+
normalized_pipes: dict[str, int] = {}
|
|
832
|
+
for name, value in pipes.items():
|
|
833
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
|
|
834
|
+
raise ValidationError(f"atomic process {name} must be a positive integer")
|
|
835
|
+
normalized_pipes[name] = value
|
|
836
|
+
require_secret_free(parameters, label="atomic process bridge parameters")
|
|
837
|
+
return freeze_mapping(
|
|
838
|
+
{
|
|
839
|
+
**dict(parameters),
|
|
840
|
+
"resource_limits": ResourceLimits(**limits),
|
|
841
|
+
"pipe_limits": normalized_pipes,
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
def _load_atomic_worker(
|
|
847
|
+
parameters: Mapping[str, Any],
|
|
848
|
+
*,
|
|
849
|
+
environment: Mapping[str, str],
|
|
850
|
+
) -> LockedProcessWorker:
|
|
851
|
+
worker = parameters["worker"]
|
|
852
|
+
return load_locked_process_worker(
|
|
853
|
+
"atomic_pair",
|
|
854
|
+
worker,
|
|
855
|
+
environment=environment,
|
|
856
|
+
checkout_environment=worker["checkout_environment"],
|
|
857
|
+
adapter_checkout_environment=worker["adapter_checkout_environment"],
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
def _parse_payload_parameters(parameters: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
862
|
+
if not isinstance(parameters, Mapping) or set(parameters) != {
|
|
863
|
+
"schema_version",
|
|
864
|
+
"protocol",
|
|
865
|
+
"allowed_release_ids",
|
|
866
|
+
"release_parameters",
|
|
867
|
+
}:
|
|
868
|
+
raise ValidationError("release payload builder parameters are invalid")
|
|
869
|
+
if (
|
|
870
|
+
parameters["schema_version"] != _SCHEMA_VERSION
|
|
871
|
+
or parameters["protocol"] != RELEASE_PAYLOAD_PROTOCOL
|
|
872
|
+
):
|
|
873
|
+
raise ValidationError("unsupported release payload builder protocol")
|
|
874
|
+
raw_ids = parameters["allowed_release_ids"]
|
|
875
|
+
if (
|
|
876
|
+
isinstance(raw_ids, (str, bytes))
|
|
877
|
+
or not isinstance(raw_ids, Sequence)
|
|
878
|
+
or not raw_ids
|
|
879
|
+
or len(raw_ids) != len(set(raw_ids))
|
|
880
|
+
or any(not isinstance(item, str) or not item.strip() for item in raw_ids)
|
|
881
|
+
):
|
|
882
|
+
raise ValidationError("allowed_release_ids must be unique non-empty strings")
|
|
883
|
+
if not isinstance(parameters["release_parameters"], Mapping):
|
|
884
|
+
raise ValidationError("release_parameters must be an object")
|
|
885
|
+
require_secret_free(parameters, label="release payload builder parameters")
|
|
886
|
+
return freeze_mapping(parameters)
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
def _parse_atomic_response(
|
|
890
|
+
text: str,
|
|
891
|
+
*,
|
|
892
|
+
expected_request_hash: str,
|
|
893
|
+
secret_values: Sequence[str] = (),
|
|
894
|
+
) -> _ParsedAtomicResponse:
|
|
895
|
+
try:
|
|
896
|
+
raw = json.loads(text)
|
|
897
|
+
except json.JSONDecodeError as exc:
|
|
898
|
+
raise ValidationError("atomic process worker returned invalid JSON") from exc
|
|
899
|
+
if not isinstance(raw, Mapping) or set(raw) != {
|
|
900
|
+
"schema_version",
|
|
901
|
+
"protocol",
|
|
902
|
+
"request_hash",
|
|
903
|
+
"receipt",
|
|
904
|
+
"arms",
|
|
905
|
+
}:
|
|
906
|
+
raise ValidationError("atomic process response fields are invalid")
|
|
907
|
+
if (
|
|
908
|
+
raw["schema_version"] != _SCHEMA_VERSION
|
|
909
|
+
or raw["protocol"] != ATOMIC_PROCESS_RESPONSE_PROTOCOL
|
|
910
|
+
or raw["request_hash"] != expected_request_hash
|
|
911
|
+
):
|
|
912
|
+
raise ValidationError("atomic process response identity is invalid")
|
|
913
|
+
require_secret_values_absent(
|
|
914
|
+
raw,
|
|
915
|
+
secret_values=secret_values,
|
|
916
|
+
label="atomic process response",
|
|
917
|
+
)
|
|
918
|
+
require_secret_free(raw, label="atomic process response")
|
|
919
|
+
receipt = _receipt_from_mapping(raw["receipt"])
|
|
920
|
+
arms_raw = raw["arms"]
|
|
921
|
+
if isinstance(arms_raw, (str, bytes)) or not isinstance(arms_raw, Sequence):
|
|
922
|
+
raise ValidationError("atomic process response arms must be an array")
|
|
923
|
+
arms = tuple(_arm_result_from_mapping(item) for item in arms_raw)
|
|
924
|
+
return _ParsedAtomicResponse(receipt=receipt, arms=arms)
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def _receipt_from_mapping(value: Any) -> AtomicReplayReceipt:
|
|
928
|
+
fields = {
|
|
929
|
+
"benchmark_id",
|
|
930
|
+
"checkpoint_id",
|
|
931
|
+
"source_revision",
|
|
932
|
+
"bridge_id",
|
|
933
|
+
"bridge_revision",
|
|
934
|
+
"scorer_id",
|
|
935
|
+
"scorer_revision",
|
|
936
|
+
"origin_state_hash",
|
|
937
|
+
"replay_identity",
|
|
938
|
+
"seed",
|
|
939
|
+
"locked_dependency_hashes",
|
|
940
|
+
"arms",
|
|
941
|
+
}
|
|
942
|
+
if not isinstance(value, Mapping) or set(value) != fields:
|
|
943
|
+
raise ValidationError("atomic process receipt fields are invalid")
|
|
944
|
+
raw_arms = value["arms"]
|
|
945
|
+
if isinstance(raw_arms, (str, bytes)) or not isinstance(raw_arms, Sequence):
|
|
946
|
+
raise ValidationError("atomic process receipt arms must be an array")
|
|
947
|
+
receipts: list[AtomicReplayArmReceipt] = []
|
|
948
|
+
arm_fields = {
|
|
949
|
+
"role",
|
|
950
|
+
"arm_id",
|
|
951
|
+
"origin_state_hash",
|
|
952
|
+
"replay_identity",
|
|
953
|
+
"seed",
|
|
954
|
+
"locked_dependency_hashes",
|
|
955
|
+
"runtime_instance_id",
|
|
956
|
+
"completed",
|
|
957
|
+
}
|
|
958
|
+
for item in raw_arms:
|
|
959
|
+
if not isinstance(item, Mapping) or set(item) != arm_fields:
|
|
960
|
+
raise ValidationError("atomic process arm receipt fields are invalid")
|
|
961
|
+
receipts.append(AtomicReplayArmReceipt(**item))
|
|
962
|
+
return AtomicReplayReceipt(
|
|
963
|
+
**{name: value[name] for name in fields - {"arms"}},
|
|
964
|
+
arms=tuple(receipts),
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
def _arm_result_from_mapping(value: Any) -> AtomicReplayArmResult:
|
|
969
|
+
if not isinstance(value, Mapping) or set(value) != {
|
|
970
|
+
"role",
|
|
971
|
+
"arm_id",
|
|
972
|
+
"metrics",
|
|
973
|
+
"trace_hash",
|
|
974
|
+
"usage",
|
|
975
|
+
}:
|
|
976
|
+
raise ValidationError("atomic process arm result fields are invalid")
|
|
977
|
+
usage = value["usage"]
|
|
978
|
+
if not isinstance(usage, Mapping) or set(usage) != _USAGE_FIELDS:
|
|
979
|
+
raise ValidationError("atomic process arm usage fields are invalid")
|
|
980
|
+
return AtomicReplayArmResult(
|
|
981
|
+
role=value["role"],
|
|
982
|
+
arm_id=value["arm_id"],
|
|
983
|
+
metrics=value["metrics"],
|
|
984
|
+
trace_hash=value["trace_hash"],
|
|
985
|
+
usage=ResourceUsage(**usage),
|
|
986
|
+
)
|
|
987
|
+
|
|
988
|
+
|
|
989
|
+
def _text(value: Any, *, label: str) -> str:
|
|
990
|
+
if (
|
|
991
|
+
not isinstance(value, str)
|
|
992
|
+
or not value.strip()
|
|
993
|
+
or value != value.strip()
|
|
994
|
+
or any(not character.isprintable() for character in value)
|
|
995
|
+
):
|
|
996
|
+
raise ValidationError(f"{label} must be trimmed non-empty printable text")
|
|
997
|
+
return value
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
def _sha256(value: Any, *, label: str) -> str:
|
|
1001
|
+
if not isinstance(value, str) or _SHA256.fullmatch(value) is None:
|
|
1002
|
+
raise ValidationError(f"{label} must be a lowercase SHA-256 digest")
|
|
1003
|
+
return value
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
__all__ = [
|
|
1007
|
+
"ATOMIC_PROCESS_BRIDGE_PROTOCOL",
|
|
1008
|
+
"ATOMIC_PROCESS_REQUEST_PROTOCOL",
|
|
1009
|
+
"ATOMIC_PROCESS_RESPONSE_PROTOCOL",
|
|
1010
|
+
"CHECKPOINT_INVENTORY_PROTOCOL",
|
|
1011
|
+
"RELEASE_PAYLOAD_PROTOCOL",
|
|
1012
|
+
"AtomicProcessBridgeBuilder",
|
|
1013
|
+
"FrozenCheckpointInventoryBuilder",
|
|
1014
|
+
"ReleaseArmPayloadBuilder",
|
|
1015
|
+
"atomic_process_bridge_builder_factory",
|
|
1016
|
+
"frozen_checkpoint_inventory_builder_factory",
|
|
1017
|
+
"release_arm_payload_builder_factory",
|
|
1018
|
+
]
|