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,1348 @@
|
|
|
1
|
+
"""Outcome-blind selector decisions and verified counterfactual result assembly.
|
|
2
|
+
|
|
3
|
+
This module intentionally separates two capabilities:
|
|
4
|
+
|
|
5
|
+
* :func:`build_selector_evaluation_input` exports only authenticated pre-action
|
|
6
|
+
feature observations, eligibility masks, and content identities.
|
|
7
|
+
* :func:`assemble_selector_campaign_results` is the only function here that
|
|
8
|
+
reads evaluator outcomes, and it accepts an already frozen decision artifact.
|
|
9
|
+
|
|
10
|
+
Consequently a selector policy never receives test outcomes through this API.
|
|
11
|
+
The diagnostic observed-action oracle is created only in the second phase and
|
|
12
|
+
is rejected from the outcome-blind decision artifact.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import random
|
|
18
|
+
import re
|
|
19
|
+
from collections import defaultdict
|
|
20
|
+
from collections.abc import Mapping, Sequence
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from statistics import fmean
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from agentinputlab.contracts.enums import SemanticFamily
|
|
26
|
+
from agentinputlab.contracts.errors import ArtifactError, ValidationError
|
|
27
|
+
from agentinputlab.contracts.serialization import content_hash, freeze_mapping, to_jsonable
|
|
28
|
+
from agentinputlab.contracts.state import FrozenAgentState
|
|
29
|
+
from agentinputlab.experiments import (
|
|
30
|
+
ExperimentManifest,
|
|
31
|
+
ExperimentStage,
|
|
32
|
+
load_experiment_manifest,
|
|
33
|
+
load_verified_cell_results,
|
|
34
|
+
verify_experiment_run_directory,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
from .artifacts import SelectorArtifactBundle, SelectorArtifactStage
|
|
38
|
+
from .campaigns import (
|
|
39
|
+
validate_representative_manifest,
|
|
40
|
+
validate_selector_campaign_design,
|
|
41
|
+
validate_selector_campaign_manifest,
|
|
42
|
+
validate_selector_campaign_results,
|
|
43
|
+
)
|
|
44
|
+
from .discovery import (
|
|
45
|
+
_convert_result_record,
|
|
46
|
+
_observation_from_artifact,
|
|
47
|
+
_observation_to_artifact,
|
|
48
|
+
_state_from_artifact,
|
|
49
|
+
_validate_pre_action_receipt,
|
|
50
|
+
)
|
|
51
|
+
from .features import VisibleStateFeatureExtractor
|
|
52
|
+
from .outcome_table import EvaluatedArmOutcome, UtilityDefinition
|
|
53
|
+
|
|
54
|
+
_INPUT_ARTIFACT = "agentinputlab.selector_evaluation_input"
|
|
55
|
+
_DECISION_ARTIFACT = "agentinputlab.selector_campaign_decisions"
|
|
56
|
+
_ZERO_SHOT_ARTIFACT = "agentinputlab.selector_zero_shot_replay"
|
|
57
|
+
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
58
|
+
_CORE_FAMILIES = (
|
|
59
|
+
SemanticFamily.REFORMULATE,
|
|
60
|
+
SemanticFamily.DECOMPOSE,
|
|
61
|
+
SemanticFamily.ACQUIRE,
|
|
62
|
+
SemanticFamily.CLARIFY,
|
|
63
|
+
SemanticFamily.CURATE,
|
|
64
|
+
)
|
|
65
|
+
_POLICY_ARTIFACT_IDS = {
|
|
66
|
+
"defect_rule",
|
|
67
|
+
"zero_shot_router",
|
|
68
|
+
"penalized_t_learner",
|
|
69
|
+
"gbdt_t_learner",
|
|
70
|
+
"eligibility_aware_selector",
|
|
71
|
+
}
|
|
72
|
+
_STANDARD_SELECTOR_KINDS = {
|
|
73
|
+
"rule",
|
|
74
|
+
"ridge_utility",
|
|
75
|
+
"ridge_t_learner",
|
|
76
|
+
"logistic_t_learner",
|
|
77
|
+
"gbdt_t_learner",
|
|
78
|
+
"knn_utility",
|
|
79
|
+
"cost_risk_guardrail",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def build_selector_evaluation_input(
|
|
84
|
+
run_dirs: Sequence[str | Path],
|
|
85
|
+
*,
|
|
86
|
+
design: Mapping[str, Any],
|
|
87
|
+
representatives: Mapping[str, Any],
|
|
88
|
+
extractor: VisibleStateFeatureExtractor,
|
|
89
|
+
expected_partial_plan_hash: str,
|
|
90
|
+
) -> Mapping[str, Any]:
|
|
91
|
+
"""Export the frozen outcome-blind selector test input.
|
|
92
|
+
|
|
93
|
+
The formal paper design is 526 three-arena common-support groups. The
|
|
94
|
+
older 500-common + 100-partial layout remains accepted for non-paper
|
|
95
|
+
library studies.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
roots = tuple(Path(item).expanduser().resolve() for item in run_dirs)
|
|
99
|
+
if not roots:
|
|
100
|
+
raise ValidationError("selector evaluation input requires verified run directories")
|
|
101
|
+
parsed_design = validate_selector_campaign_design(design)
|
|
102
|
+
frozen_representatives = validate_representative_manifest(representatives)
|
|
103
|
+
_require_sha256(expected_partial_plan_hash, label="partial-support plan hash")
|
|
104
|
+
if not isinstance(extractor, VisibleStateFeatureExtractor):
|
|
105
|
+
raise TypeError("selector evaluation requires VisibleStateFeatureExtractor")
|
|
106
|
+
rank_one = _rank_one_releases(frozen_representatives)
|
|
107
|
+
|
|
108
|
+
source_runs: list[dict[str, str]] = []
|
|
109
|
+
groups: dict[str, dict[str, Any]] = {}
|
|
110
|
+
seen_cells: set[tuple[str, str]] = set()
|
|
111
|
+
common_run_count = 0
|
|
112
|
+
partial_run_count = 0
|
|
113
|
+
for root in roots:
|
|
114
|
+
manifest, records, source = _verified_source(root)
|
|
115
|
+
common_support = _validate_evaluation_manifest(
|
|
116
|
+
manifest,
|
|
117
|
+
representative_manifest_hash=frozen_representatives["manifest_hash"],
|
|
118
|
+
rank_one=rank_one,
|
|
119
|
+
expected_partial_plan_hash=expected_partial_plan_hash,
|
|
120
|
+
)
|
|
121
|
+
common_run_count += int(common_support)
|
|
122
|
+
partial_run_count += int(not common_support)
|
|
123
|
+
source_runs.append(source)
|
|
124
|
+
arm_by_id = {arm.arm_id: arm for arm in manifest.arms}
|
|
125
|
+
for record in records:
|
|
126
|
+
pre_action = _pre_action_record(
|
|
127
|
+
record,
|
|
128
|
+
arm_by_id=arm_by_id,
|
|
129
|
+
extractor=extractor,
|
|
130
|
+
common_support=common_support,
|
|
131
|
+
)
|
|
132
|
+
group_id = pre_action["group_id"]
|
|
133
|
+
cell_key = (group_id, pre_action["model_slot"])
|
|
134
|
+
if cell_key in seen_cells:
|
|
135
|
+
raise ValidationError("selector evaluation contains duplicate group/model cells")
|
|
136
|
+
seen_cells.add(cell_key)
|
|
137
|
+
existing = groups.get(group_id)
|
|
138
|
+
if existing is None:
|
|
139
|
+
groups[group_id] = {
|
|
140
|
+
"group_id": group_id,
|
|
141
|
+
"suite_id": pre_action["suite_id"],
|
|
142
|
+
"common_support": common_support,
|
|
143
|
+
"state_hash": pre_action["state_hash"],
|
|
144
|
+
"observation": pre_action["observation"],
|
|
145
|
+
"eligible_families": pre_action["eligible_families"],
|
|
146
|
+
"model_slots": [pre_action["model_slot"]],
|
|
147
|
+
"seed": pre_action["seed"],
|
|
148
|
+
}
|
|
149
|
+
continue
|
|
150
|
+
comparable = {
|
|
151
|
+
key: existing[key]
|
|
152
|
+
for key in (
|
|
153
|
+
"suite_id",
|
|
154
|
+
"common_support",
|
|
155
|
+
"state_hash",
|
|
156
|
+
"observation",
|
|
157
|
+
"eligible_families",
|
|
158
|
+
"seed",
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
if comparable != {
|
|
162
|
+
"suite_id": pre_action["suite_id"],
|
|
163
|
+
"common_support": common_support,
|
|
164
|
+
"state_hash": pre_action["state_hash"],
|
|
165
|
+
"observation": pre_action["observation"],
|
|
166
|
+
"eligible_families": pre_action["eligible_families"],
|
|
167
|
+
"seed": pre_action["seed"],
|
|
168
|
+
}:
|
|
169
|
+
raise ValidationError(
|
|
170
|
+
"selector group changed pre-action state, features, eligibility, or suite"
|
|
171
|
+
)
|
|
172
|
+
existing["model_slots"].append(pre_action["model_slot"])
|
|
173
|
+
|
|
174
|
+
expected_groups = parsed_design["required_independent_groups"]
|
|
175
|
+
expected_common = parsed_design["minimum_common_support_groups"]
|
|
176
|
+
common_groups = sum(bool(group["common_support"]) for group in groups.values())
|
|
177
|
+
if len(groups) != expected_groups or common_groups != expected_common:
|
|
178
|
+
raise ValidationError(
|
|
179
|
+
"selector evaluation support differs from the frozen design "
|
|
180
|
+
f"({len(groups)} total/{common_groups} common)"
|
|
181
|
+
)
|
|
182
|
+
if set(group["suite_id"] for group in groups.values()) != set(parsed_design["suite_ids"]):
|
|
183
|
+
raise ValidationError("selector evaluation suites differ from the frozen design")
|
|
184
|
+
expected_model_slots = ["anchor_a", "anchor_b"]
|
|
185
|
+
for group in groups.values():
|
|
186
|
+
group["model_slots"] = sorted(group["model_slots"])
|
|
187
|
+
if (
|
|
188
|
+
group["model_slots"] != expected_model_slots
|
|
189
|
+
or group["seed"] != 17
|
|
190
|
+
or len(group["eligible_families"]) != (5 if group["common_support"] else 3)
|
|
191
|
+
):
|
|
192
|
+
raise ValidationError(
|
|
193
|
+
"selector evaluation group differs from the two-model, one-seed "
|
|
194
|
+
"common/partial support protocol"
|
|
195
|
+
)
|
|
196
|
+
if expected_groups == 600 and expected_common == 500:
|
|
197
|
+
if common_run_count != 1 or partial_run_count != 10:
|
|
198
|
+
raise ValidationError(
|
|
199
|
+
"600-group selector evaluation requires one common and ten partial runs"
|
|
200
|
+
)
|
|
201
|
+
partial_groups = expected_groups - expected_common
|
|
202
|
+
if partial_groups != 100:
|
|
203
|
+
raise ValidationError("selector partial-support block must contain 100 groups")
|
|
204
|
+
|
|
205
|
+
design_payload = _jsonable_mapping(parsed_design)
|
|
206
|
+
source_payload = sorted(source_runs, key=lambda item: item["run_identity_hash"])
|
|
207
|
+
group_payload = [groups[group_id] for group_id in sorted(groups)]
|
|
208
|
+
base = {
|
|
209
|
+
"schema_version": 1,
|
|
210
|
+
"artifact": _INPUT_ARTIFACT,
|
|
211
|
+
"stage": "frozen",
|
|
212
|
+
"data_status": "VERIFIED_PRE_ACTION_INPUTS",
|
|
213
|
+
"is_synthetic": False,
|
|
214
|
+
"campaign_design": design_payload,
|
|
215
|
+
"campaign_design_hash": content_hash(design_payload),
|
|
216
|
+
"representative_manifest_hash": frozen_representatives["manifest_hash"],
|
|
217
|
+
"partial_support_plan_hash": expected_partial_plan_hash,
|
|
218
|
+
"source_runs": source_payload,
|
|
219
|
+
"extractor": {
|
|
220
|
+
"id": extractor.extractor_id,
|
|
221
|
+
"revision": extractor.extractor_revision,
|
|
222
|
+
"sha256": extractor.extractor_hash,
|
|
223
|
+
"feature_schema_id": extractor.feature_schema_id,
|
|
224
|
+
"feature_schema_sha256": extractor.feature_schema_hash,
|
|
225
|
+
},
|
|
226
|
+
"groups": group_payload,
|
|
227
|
+
"outcomes_included": False,
|
|
228
|
+
"other_arm_outcomes_accessible": False,
|
|
229
|
+
}
|
|
230
|
+
artifact = {**base, "input_hash": content_hash(base)}
|
|
231
|
+
return validate_selector_evaluation_input(artifact)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def validate_selector_evaluation_input(
|
|
235
|
+
value: Mapping[str, Any],
|
|
236
|
+
) -> Mapping[str, Any]:
|
|
237
|
+
"""Validate and freeze an outcome-free selector evaluation input."""
|
|
238
|
+
|
|
239
|
+
required = {
|
|
240
|
+
"schema_version",
|
|
241
|
+
"artifact",
|
|
242
|
+
"stage",
|
|
243
|
+
"data_status",
|
|
244
|
+
"is_synthetic",
|
|
245
|
+
"campaign_design",
|
|
246
|
+
"campaign_design_hash",
|
|
247
|
+
"representative_manifest_hash",
|
|
248
|
+
"partial_support_plan_hash",
|
|
249
|
+
"source_runs",
|
|
250
|
+
"extractor",
|
|
251
|
+
"groups",
|
|
252
|
+
"outcomes_included",
|
|
253
|
+
"other_arm_outcomes_accessible",
|
|
254
|
+
"input_hash",
|
|
255
|
+
}
|
|
256
|
+
data = _strict_mapping(value, required, label="selector evaluation input")
|
|
257
|
+
if (
|
|
258
|
+
data["schema_version"] != 1
|
|
259
|
+
or data["artifact"] != _INPUT_ARTIFACT
|
|
260
|
+
or data["stage"] != "frozen"
|
|
261
|
+
or data["data_status"] != "VERIFIED_PRE_ACTION_INPUTS"
|
|
262
|
+
or data["is_synthetic"] is not False
|
|
263
|
+
or data["outcomes_included"] is not False
|
|
264
|
+
or data["other_arm_outcomes_accessible"] is not False
|
|
265
|
+
):
|
|
266
|
+
raise ValidationError("selector evaluation input identity or leakage boundary is invalid")
|
|
267
|
+
design = validate_selector_campaign_design(
|
|
268
|
+
_mapping(data["campaign_design"], label="campaign_design")
|
|
269
|
+
)
|
|
270
|
+
design_payload = _jsonable_mapping(design)
|
|
271
|
+
if content_hash(design_payload) != data["campaign_design_hash"]:
|
|
272
|
+
raise ValidationError("selector evaluation campaign design hash mismatch")
|
|
273
|
+
for name in (
|
|
274
|
+
"campaign_design_hash",
|
|
275
|
+
"representative_manifest_hash",
|
|
276
|
+
"partial_support_plan_hash",
|
|
277
|
+
"input_hash",
|
|
278
|
+
):
|
|
279
|
+
_require_sha256(data[name], label=name)
|
|
280
|
+
extractor = _strict_mapping(
|
|
281
|
+
data["extractor"],
|
|
282
|
+
{
|
|
283
|
+
"id",
|
|
284
|
+
"revision",
|
|
285
|
+
"sha256",
|
|
286
|
+
"feature_schema_id",
|
|
287
|
+
"feature_schema_sha256",
|
|
288
|
+
},
|
|
289
|
+
label="selector evaluation extractor",
|
|
290
|
+
)
|
|
291
|
+
for name in ("id", "revision", "feature_schema_id"):
|
|
292
|
+
_text(extractor[name], label=f"extractor.{name}")
|
|
293
|
+
for name in ("sha256", "feature_schema_sha256"):
|
|
294
|
+
_require_sha256(extractor[name], label=f"extractor.{name}")
|
|
295
|
+
sources = _validate_sources(data["source_runs"])
|
|
296
|
+
if not sources:
|
|
297
|
+
raise ValidationError("selector evaluation input requires verified source runs")
|
|
298
|
+
groups_raw = _sequence(data["groups"], label="selector evaluation groups")
|
|
299
|
+
groups: list[dict[str, Any]] = []
|
|
300
|
+
seen: set[str] = set()
|
|
301
|
+
common_count = 0
|
|
302
|
+
suites: set[str] = set()
|
|
303
|
+
for index, raw in enumerate(groups_raw):
|
|
304
|
+
group = _strict_mapping(
|
|
305
|
+
raw,
|
|
306
|
+
{
|
|
307
|
+
"group_id",
|
|
308
|
+
"suite_id",
|
|
309
|
+
"common_support",
|
|
310
|
+
"state_hash",
|
|
311
|
+
"observation",
|
|
312
|
+
"eligible_families",
|
|
313
|
+
"model_slots",
|
|
314
|
+
"seed",
|
|
315
|
+
},
|
|
316
|
+
label=f"selector evaluation group {index}",
|
|
317
|
+
)
|
|
318
|
+
group_id = _group_id(group["group_id"], label="selector evaluation group ID")
|
|
319
|
+
if group_id in seen:
|
|
320
|
+
raise ValidationError("selector evaluation group IDs must be unique")
|
|
321
|
+
seen.add(group_id)
|
|
322
|
+
suite_id = _text(group["suite_id"], label="selector evaluation suite")
|
|
323
|
+
if suite_id not in design["suite_ids"]:
|
|
324
|
+
raise ValidationError("selector evaluation group suite is undeclared")
|
|
325
|
+
suites.add(suite_id)
|
|
326
|
+
common_support = _boolean(
|
|
327
|
+
group["common_support"], label="selector evaluation common_support"
|
|
328
|
+
)
|
|
329
|
+
common_count += int(common_support)
|
|
330
|
+
_require_sha256(group["state_hash"], label="selector evaluation state_hash")
|
|
331
|
+
observation = _observation_from_artifact(to_jsonable(group["observation"]))
|
|
332
|
+
if observation.state_hash != group["state_hash"]:
|
|
333
|
+
raise ValidationError("selector evaluation observation is bound to another state")
|
|
334
|
+
if (
|
|
335
|
+
observation.extractor_id != extractor["id"]
|
|
336
|
+
or observation.extractor_revision != extractor["revision"]
|
|
337
|
+
):
|
|
338
|
+
raise ValidationError("selector evaluation observation changed extractor identity")
|
|
339
|
+
families = _family_sequence(group["eligible_families"])
|
|
340
|
+
if len(families) != (5 if common_support else 3):
|
|
341
|
+
raise ValidationError("selector evaluation eligibility cardinality is invalid")
|
|
342
|
+
model_slots = tuple(
|
|
343
|
+
_text(item, label="selector evaluation model slot")
|
|
344
|
+
for item in _sequence(group["model_slots"], label="selector model slots")
|
|
345
|
+
)
|
|
346
|
+
if model_slots != ("anchor_a", "anchor_b"):
|
|
347
|
+
raise ValidationError("selector evaluation requires exact anchor_a/anchor_b slots")
|
|
348
|
+
if group["seed"] != 17:
|
|
349
|
+
raise ValidationError("selector evaluation requires continuation seed 17")
|
|
350
|
+
groups.append(
|
|
351
|
+
{
|
|
352
|
+
**group,
|
|
353
|
+
"observation": _observation_to_artifact(observation),
|
|
354
|
+
"eligible_families": [family.value for family in families],
|
|
355
|
+
"model_slots": list(model_slots),
|
|
356
|
+
}
|
|
357
|
+
)
|
|
358
|
+
if (
|
|
359
|
+
len(groups) != design["required_independent_groups"]
|
|
360
|
+
or common_count != design["minimum_common_support_groups"]
|
|
361
|
+
or suites != set(design["suite_ids"])
|
|
362
|
+
):
|
|
363
|
+
raise ValidationError("selector evaluation group support differs from campaign design")
|
|
364
|
+
if (
|
|
365
|
+
content_hash({key: item for key, item in data.items() if key != "input_hash"})
|
|
366
|
+
!= data["input_hash"]
|
|
367
|
+
):
|
|
368
|
+
raise ValidationError("selector evaluation input hash differs from its content")
|
|
369
|
+
return freeze_mapping(
|
|
370
|
+
{
|
|
371
|
+
**data,
|
|
372
|
+
"campaign_design": design_payload,
|
|
373
|
+
"source_runs": sources,
|
|
374
|
+
"groups": tuple(groups),
|
|
375
|
+
}
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def freeze_zero_shot_router_replay(
|
|
380
|
+
evaluation_input: Mapping[str, Any],
|
|
381
|
+
decisions: Sequence[Mapping[str, Any]],
|
|
382
|
+
*,
|
|
383
|
+
selector_id: str,
|
|
384
|
+
revision: str,
|
|
385
|
+
router_prompt_sha256: str,
|
|
386
|
+
backend_sha256: str,
|
|
387
|
+
routing_run_sha256: str,
|
|
388
|
+
) -> Mapping[str, Any]:
|
|
389
|
+
"""Freeze externally generated, outcome-blind zero-shot routing choices."""
|
|
390
|
+
|
|
391
|
+
inputs = validate_selector_evaluation_input(evaluation_input)
|
|
392
|
+
for name, digest in (
|
|
393
|
+
("router prompt", router_prompt_sha256),
|
|
394
|
+
("backend", backend_sha256),
|
|
395
|
+
("routing run", routing_run_sha256),
|
|
396
|
+
):
|
|
397
|
+
_require_sha256(digest, label=f"zero-shot {name}")
|
|
398
|
+
_text(selector_id, label="zero-shot selector ID")
|
|
399
|
+
_text(revision, label="zero-shot selector revision")
|
|
400
|
+
groups = {group["group_id"]: group for group in inputs["groups"]}
|
|
401
|
+
rows: list[dict[str, Any]] = []
|
|
402
|
+
seen: set[str] = set()
|
|
403
|
+
for index, raw in enumerate(decisions):
|
|
404
|
+
row = _strict_mapping(
|
|
405
|
+
raw,
|
|
406
|
+
{"group_id", "selected_family", "selected_release_id"},
|
|
407
|
+
label=f"zero-shot decision {index}",
|
|
408
|
+
)
|
|
409
|
+
group_id = _group_id(row["group_id"], label="zero-shot group ID")
|
|
410
|
+
if group_id in seen or group_id not in groups:
|
|
411
|
+
raise ValidationError("zero-shot decisions have duplicate or unknown groups")
|
|
412
|
+
seen.add(group_id)
|
|
413
|
+
family = _family(row["selected_family"])
|
|
414
|
+
release_id = _text(row["selected_release_id"], label="zero-shot release")
|
|
415
|
+
_validate_selected_action(group=groups[group_id], family=family, release_id=release_id)
|
|
416
|
+
rows.append(
|
|
417
|
+
{
|
|
418
|
+
"group_id": group_id,
|
|
419
|
+
"selected_family": family.value,
|
|
420
|
+
"selected_release_id": release_id,
|
|
421
|
+
}
|
|
422
|
+
)
|
|
423
|
+
if seen != set(groups):
|
|
424
|
+
raise ValidationError("zero-shot replay must contain one choice for every input group")
|
|
425
|
+
base = {
|
|
426
|
+
"schema_version": 1,
|
|
427
|
+
"artifact": _ZERO_SHOT_ARTIFACT,
|
|
428
|
+
"stage": "frozen",
|
|
429
|
+
"selector_kind": "zero_shot_router",
|
|
430
|
+
"selector_id": selector_id,
|
|
431
|
+
"revision": revision,
|
|
432
|
+
"evaluation_input_hash": inputs["input_hash"],
|
|
433
|
+
"attestation": {
|
|
434
|
+
"router_prompt_sha256": router_prompt_sha256,
|
|
435
|
+
"backend_sha256": backend_sha256,
|
|
436
|
+
"routing_run_sha256": routing_run_sha256,
|
|
437
|
+
"outcomes_accessed": False,
|
|
438
|
+
"other_arm_outcomes_accessed": False,
|
|
439
|
+
},
|
|
440
|
+
"decisions": sorted(rows, key=lambda item: item["group_id"]),
|
|
441
|
+
}
|
|
442
|
+
return freeze_mapping({**base, "bundle_hash": content_hash(base)})
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def validate_zero_shot_router_replay(
|
|
446
|
+
evaluation_input: Mapping[str, Any],
|
|
447
|
+
value: Mapping[str, Any],
|
|
448
|
+
) -> Mapping[str, Any]:
|
|
449
|
+
inputs = validate_selector_evaluation_input(evaluation_input)
|
|
450
|
+
required = {
|
|
451
|
+
"schema_version",
|
|
452
|
+
"artifact",
|
|
453
|
+
"stage",
|
|
454
|
+
"selector_kind",
|
|
455
|
+
"selector_id",
|
|
456
|
+
"revision",
|
|
457
|
+
"evaluation_input_hash",
|
|
458
|
+
"attestation",
|
|
459
|
+
"decisions",
|
|
460
|
+
"bundle_hash",
|
|
461
|
+
}
|
|
462
|
+
data = _strict_mapping(value, required, label="zero-shot replay")
|
|
463
|
+
if (
|
|
464
|
+
data["schema_version"] != 1
|
|
465
|
+
or data["artifact"] != _ZERO_SHOT_ARTIFACT
|
|
466
|
+
or data["stage"] != "frozen"
|
|
467
|
+
or data["selector_kind"] != "zero_shot_router"
|
|
468
|
+
or data["evaluation_input_hash"] != inputs["input_hash"]
|
|
469
|
+
):
|
|
470
|
+
raise ValidationError("zero-shot replay identity differs from its evaluation input")
|
|
471
|
+
_text(data["selector_id"], label="zero-shot selector ID")
|
|
472
|
+
_text(data["revision"], label="zero-shot selector revision")
|
|
473
|
+
attestation = _strict_mapping(
|
|
474
|
+
data["attestation"],
|
|
475
|
+
{
|
|
476
|
+
"router_prompt_sha256",
|
|
477
|
+
"backend_sha256",
|
|
478
|
+
"routing_run_sha256",
|
|
479
|
+
"outcomes_accessed",
|
|
480
|
+
"other_arm_outcomes_accessed",
|
|
481
|
+
},
|
|
482
|
+
label="zero-shot attestation",
|
|
483
|
+
)
|
|
484
|
+
for name in ("router_prompt_sha256", "backend_sha256", "routing_run_sha256"):
|
|
485
|
+
_require_sha256(attestation[name], label=f"zero-shot {name}")
|
|
486
|
+
if (
|
|
487
|
+
attestation["outcomes_accessed"] is not False
|
|
488
|
+
or attestation["other_arm_outcomes_accessed"] is not False
|
|
489
|
+
):
|
|
490
|
+
raise ValidationError("zero-shot replay may not access test outcomes")
|
|
491
|
+
rebuilt = freeze_zero_shot_router_replay(
|
|
492
|
+
inputs,
|
|
493
|
+
_sequence(data["decisions"], label="zero-shot decisions"),
|
|
494
|
+
selector_id=data["selector_id"],
|
|
495
|
+
revision=data["revision"],
|
|
496
|
+
router_prompt_sha256=attestation["router_prompt_sha256"],
|
|
497
|
+
backend_sha256=attestation["backend_sha256"],
|
|
498
|
+
routing_run_sha256=attestation["routing_run_sha256"],
|
|
499
|
+
)
|
|
500
|
+
if rebuilt["bundle_hash"] != data["bundle_hash"]:
|
|
501
|
+
raise ValidationError("zero-shot replay hash differs from its content")
|
|
502
|
+
return freeze_mapping(data)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def build_selector_campaign_decisions(
|
|
506
|
+
campaign: Mapping[str, Any],
|
|
507
|
+
evaluation_input: Mapping[str, Any],
|
|
508
|
+
policy_artifacts: Mapping[str, Mapping[str, Any]],
|
|
509
|
+
) -> Mapping[str, Any]:
|
|
510
|
+
"""Run 13 outcome-blind policies; the diagnostic oracle is deliberately absent."""
|
|
511
|
+
|
|
512
|
+
manifest = validate_selector_campaign_manifest(campaign)
|
|
513
|
+
inputs = validate_selector_evaluation_input(evaluation_input)
|
|
514
|
+
_validate_campaign_input_binding(manifest, inputs)
|
|
515
|
+
if set(policy_artifacts) != _POLICY_ARTIFACT_IDS:
|
|
516
|
+
raise ValidationError(
|
|
517
|
+
"selector decision builder requires exact artifacts for five frozen policies"
|
|
518
|
+
)
|
|
519
|
+
policies = {policy["policy_id"]: policy for policy in manifest["policies"]}
|
|
520
|
+
rank_one = _campaign_rank_one_releases(manifest)
|
|
521
|
+
loaded: dict[str, Any] = {}
|
|
522
|
+
artifact_hashes: dict[str, str] = {}
|
|
523
|
+
for policy_id in sorted(_POLICY_ARTIFACT_IDS):
|
|
524
|
+
policy = policies[policy_id]
|
|
525
|
+
binding = policy["parameters"]["artifact_binding"]
|
|
526
|
+
artifact = _mapping(
|
|
527
|
+
policy_artifacts[policy_id],
|
|
528
|
+
label=f"selector artifact {policy_id}",
|
|
529
|
+
)
|
|
530
|
+
if binding["selector_kind"] == "zero_shot_router":
|
|
531
|
+
replay = validate_zero_shot_router_replay(inputs, artifact)
|
|
532
|
+
if (
|
|
533
|
+
replay["selector_id"] != binding["selector_id"]
|
|
534
|
+
or replay["revision"] != binding["revision"]
|
|
535
|
+
or replay["bundle_hash"] != binding["sha256"]
|
|
536
|
+
):
|
|
537
|
+
raise ValidationError("zero-shot replay differs from its campaign binding")
|
|
538
|
+
loaded[policy_id] = {row["group_id"]: row for row in replay["decisions"]}
|
|
539
|
+
artifact_hashes[policy_id] = replay["bundle_hash"]
|
|
540
|
+
continue
|
|
541
|
+
bundle = SelectorArtifactBundle.from_artifact(artifact)
|
|
542
|
+
selector_kind = bundle.model_payload.get("selector_kind")
|
|
543
|
+
if (
|
|
544
|
+
bundle.stage is not SelectorArtifactStage.FROZEN
|
|
545
|
+
or selector_kind != binding["selector_kind"]
|
|
546
|
+
or selector_kind not in _STANDARD_SELECTOR_KINDS
|
|
547
|
+
or bundle.selector_card_id != binding["selector_id"]
|
|
548
|
+
or bundle.revision != binding["revision"]
|
|
549
|
+
or bundle.bundle_hash != binding["sha256"]
|
|
550
|
+
):
|
|
551
|
+
raise ValidationError(f"selector artifact {policy_id} differs from its binding")
|
|
552
|
+
input_extractor = inputs["extractor"]
|
|
553
|
+
if (
|
|
554
|
+
bundle.extractor_id != input_extractor["id"]
|
|
555
|
+
or bundle.extractor_revision != input_extractor["revision"]
|
|
556
|
+
or bundle.extractor_hash != input_extractor["sha256"]
|
|
557
|
+
or bundle.feature_schema_id != input_extractor["feature_schema_id"]
|
|
558
|
+
or bundle.feature_schema_hash != input_extractor["feature_schema_sha256"]
|
|
559
|
+
or dict(bundle.representative_by_family) != rank_one
|
|
560
|
+
):
|
|
561
|
+
raise ValidationError(
|
|
562
|
+
f"selector artifact {policy_id} changed features or representatives"
|
|
563
|
+
)
|
|
564
|
+
loaded[policy_id] = bundle.selector()
|
|
565
|
+
artifact_hashes[policy_id] = bundle.bundle_hash
|
|
566
|
+
|
|
567
|
+
rows: list[dict[str, Any]] = []
|
|
568
|
+
oracle_policy_ids = {
|
|
569
|
+
policy_id
|
|
570
|
+
for policy_id, policy in policies.items()
|
|
571
|
+
if policy["kind"] == "observed_action_oracle"
|
|
572
|
+
}
|
|
573
|
+
if oracle_policy_ids != {"observed_action_oracle"}:
|
|
574
|
+
raise ValidationError("selector campaign diagnostic oracle contract is invalid")
|
|
575
|
+
for group in inputs["groups"]:
|
|
576
|
+
group_id = group["group_id"]
|
|
577
|
+
eligible = tuple(SemanticFamily(item) for item in group["eligible_families"])
|
|
578
|
+
observation = _observation_from_artifact(to_jsonable(group["observation"]))
|
|
579
|
+
state = FrozenAgentState(
|
|
580
|
+
query="[outcome-blind selector feature evaluation]",
|
|
581
|
+
task_id=group_id,
|
|
582
|
+
)
|
|
583
|
+
for policy_id, policy in policies.items():
|
|
584
|
+
if policy_id in oracle_policy_ids:
|
|
585
|
+
continue
|
|
586
|
+
family, release_id = _select_policy_action(
|
|
587
|
+
policy,
|
|
588
|
+
group=group,
|
|
589
|
+
state=state,
|
|
590
|
+
features=observation.features,
|
|
591
|
+
eligible=eligible,
|
|
592
|
+
rank_one=rank_one,
|
|
593
|
+
loaded=loaded,
|
|
594
|
+
random_seed=manifest["metadata"]["random_seed"],
|
|
595
|
+
)
|
|
596
|
+
expected_release = _release_for(family, rank_one)[1]
|
|
597
|
+
if release_id != expected_release:
|
|
598
|
+
raise ValidationError(
|
|
599
|
+
f"selector policy {policy_id} changed the frozen representative release"
|
|
600
|
+
)
|
|
601
|
+
rows.append(
|
|
602
|
+
{
|
|
603
|
+
"group_id": group_id,
|
|
604
|
+
"policy_id": policy_id,
|
|
605
|
+
"selected_family": family.value,
|
|
606
|
+
"selected_release_id": release_id,
|
|
607
|
+
"selection_eligible": True,
|
|
608
|
+
"diagnostic_only": policy["diagnostic_only"],
|
|
609
|
+
}
|
|
610
|
+
)
|
|
611
|
+
base = {
|
|
612
|
+
"schema_version": 1,
|
|
613
|
+
"artifact": _DECISION_ARTIFACT,
|
|
614
|
+
"stage": "frozen",
|
|
615
|
+
"data_status": "OUTCOME_BLIND_DECISIONS",
|
|
616
|
+
"is_synthetic": False,
|
|
617
|
+
"campaign_manifest_hash": manifest["campaign_hash"],
|
|
618
|
+
"evaluation_input_hash": inputs["input_hash"],
|
|
619
|
+
"policy_artifact_hashes": artifact_hashes,
|
|
620
|
+
"records": sorted(rows, key=lambda row: (row["group_id"], row["policy_id"])),
|
|
621
|
+
"outcomes_accessed": False,
|
|
622
|
+
"other_arm_outcomes_accessed": False,
|
|
623
|
+
"diagnostic_oracle_included": False,
|
|
624
|
+
}
|
|
625
|
+
artifact = freeze_mapping({**base, "decision_hash": content_hash(base)})
|
|
626
|
+
return validate_selector_campaign_decisions(manifest, inputs, artifact)
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def validate_selector_campaign_decisions(
|
|
630
|
+
campaign: Mapping[str, Any],
|
|
631
|
+
evaluation_input: Mapping[str, Any],
|
|
632
|
+
decisions: Mapping[str, Any],
|
|
633
|
+
) -> Mapping[str, Any]:
|
|
634
|
+
manifest = validate_selector_campaign_manifest(campaign)
|
|
635
|
+
inputs = validate_selector_evaluation_input(evaluation_input)
|
|
636
|
+
_validate_campaign_input_binding(manifest, inputs)
|
|
637
|
+
required = {
|
|
638
|
+
"schema_version",
|
|
639
|
+
"artifact",
|
|
640
|
+
"stage",
|
|
641
|
+
"data_status",
|
|
642
|
+
"is_synthetic",
|
|
643
|
+
"campaign_manifest_hash",
|
|
644
|
+
"evaluation_input_hash",
|
|
645
|
+
"policy_artifact_hashes",
|
|
646
|
+
"records",
|
|
647
|
+
"outcomes_accessed",
|
|
648
|
+
"other_arm_outcomes_accessed",
|
|
649
|
+
"diagnostic_oracle_included",
|
|
650
|
+
"decision_hash",
|
|
651
|
+
}
|
|
652
|
+
data = _strict_mapping(decisions, required, label="selector campaign decisions")
|
|
653
|
+
if (
|
|
654
|
+
data["schema_version"] != 1
|
|
655
|
+
or data["artifact"] != _DECISION_ARTIFACT
|
|
656
|
+
or data["stage"] != "frozen"
|
|
657
|
+
or data["data_status"] != "OUTCOME_BLIND_DECISIONS"
|
|
658
|
+
or data["is_synthetic"] is not False
|
|
659
|
+
or data["campaign_manifest_hash"] != manifest["campaign_hash"]
|
|
660
|
+
or data["evaluation_input_hash"] != inputs["input_hash"]
|
|
661
|
+
or data["outcomes_accessed"] is not False
|
|
662
|
+
or data["other_arm_outcomes_accessed"] is not False
|
|
663
|
+
or data["diagnostic_oracle_included"] is not False
|
|
664
|
+
):
|
|
665
|
+
raise ValidationError("selector decision artifact violates the outcome-blind boundary")
|
|
666
|
+
hashes = _mapping(data["policy_artifact_hashes"], label="selector policy hashes")
|
|
667
|
+
if set(hashes) != _POLICY_ARTIFACT_IDS:
|
|
668
|
+
raise ValidationError("selector decision artifact policy hashes are incomplete")
|
|
669
|
+
for digest in hashes.values():
|
|
670
|
+
_require_sha256(digest, label="selector policy artifact hash")
|
|
671
|
+
policies = {
|
|
672
|
+
policy["policy_id"]: policy
|
|
673
|
+
for policy in manifest["policies"]
|
|
674
|
+
if policy["kind"] != "observed_action_oracle"
|
|
675
|
+
}
|
|
676
|
+
groups = {group["group_id"]: group for group in inputs["groups"]}
|
|
677
|
+
rank_one = _campaign_rank_one_releases(manifest)
|
|
678
|
+
seen: set[tuple[str, str]] = set()
|
|
679
|
+
parsed_rows: list[dict[str, Any]] = []
|
|
680
|
+
for index, raw in enumerate(_sequence(data["records"], label="selector decisions")):
|
|
681
|
+
row = _strict_mapping(
|
|
682
|
+
raw,
|
|
683
|
+
{
|
|
684
|
+
"group_id",
|
|
685
|
+
"policy_id",
|
|
686
|
+
"selected_family",
|
|
687
|
+
"selected_release_id",
|
|
688
|
+
"selection_eligible",
|
|
689
|
+
"diagnostic_only",
|
|
690
|
+
},
|
|
691
|
+
label=f"selector decision {index}",
|
|
692
|
+
)
|
|
693
|
+
group_id = _group_id(row["group_id"], label="selector decision group ID")
|
|
694
|
+
policy_id = _text(row["policy_id"], label="selector decision policy ID")
|
|
695
|
+
if group_id not in groups or policy_id not in policies:
|
|
696
|
+
raise ValidationError("selector decision references an unknown group or policy")
|
|
697
|
+
key = (group_id, policy_id)
|
|
698
|
+
if key in seen:
|
|
699
|
+
raise ValidationError("selector decisions contain duplicate group-policy rows")
|
|
700
|
+
seen.add(key)
|
|
701
|
+
family = _family(row["selected_family"])
|
|
702
|
+
release_id = _text(row["selected_release_id"], label="selector decision release")
|
|
703
|
+
_validate_selected_action(
|
|
704
|
+
group=groups[group_id],
|
|
705
|
+
family=family,
|
|
706
|
+
release_id=release_id,
|
|
707
|
+
)
|
|
708
|
+
if release_id != _release_for(family, rank_one)[1]:
|
|
709
|
+
raise ValidationError(
|
|
710
|
+
"selector decision release differs from the campaign representative"
|
|
711
|
+
)
|
|
712
|
+
if (
|
|
713
|
+
row["selection_eligible"] is not True
|
|
714
|
+
or row["diagnostic_only"] is not policies[policy_id]["diagnostic_only"]
|
|
715
|
+
):
|
|
716
|
+
raise ValidationError("selector decision eligibility or policy role is invalid")
|
|
717
|
+
parsed_rows.append(
|
|
718
|
+
{
|
|
719
|
+
**row,
|
|
720
|
+
"selected_family": family.value,
|
|
721
|
+
}
|
|
722
|
+
)
|
|
723
|
+
expected = {(group_id, policy_id) for group_id in groups for policy_id in policies}
|
|
724
|
+
if seen != expected:
|
|
725
|
+
raise ValidationError("selector decision artifact lacks the complete non-oracle grid")
|
|
726
|
+
_require_sha256(data["decision_hash"], label="selector decision hash")
|
|
727
|
+
if (
|
|
728
|
+
content_hash({key: item for key, item in data.items() if key != "decision_hash"})
|
|
729
|
+
!= data["decision_hash"]
|
|
730
|
+
):
|
|
731
|
+
raise ValidationError("selector decision hash differs from its content")
|
|
732
|
+
return freeze_mapping({**data, "records": tuple(parsed_rows)})
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def assemble_selector_campaign_results(
|
|
736
|
+
campaign: Mapping[str, Any],
|
|
737
|
+
evaluation_input: Mapping[str, Any],
|
|
738
|
+
decisions: Mapping[str, Any],
|
|
739
|
+
run_dirs: Sequence[str | Path],
|
|
740
|
+
*,
|
|
741
|
+
extractor: VisibleStateFeatureExtractor,
|
|
742
|
+
utility_definition: UtilityDefinition,
|
|
743
|
+
) -> Mapping[str, Any]:
|
|
744
|
+
"""Join frozen choices to verified outcomes and add the diagnostic oracle."""
|
|
745
|
+
|
|
746
|
+
manifest = validate_selector_campaign_manifest(campaign)
|
|
747
|
+
inputs = validate_selector_evaluation_input(evaluation_input)
|
|
748
|
+
frozen_decisions = validate_selector_campaign_decisions(manifest, inputs, decisions)
|
|
749
|
+
if utility_definition.utility_metric != manifest["primary_metric"]:
|
|
750
|
+
raise ValidationError("selector result utility differs from the campaign primary metric")
|
|
751
|
+
input_extractor = inputs["extractor"]
|
|
752
|
+
if (
|
|
753
|
+
extractor.extractor_id != input_extractor["id"]
|
|
754
|
+
or extractor.extractor_revision != input_extractor["revision"]
|
|
755
|
+
or extractor.extractor_hash != input_extractor["sha256"]
|
|
756
|
+
or extractor.feature_schema_id != input_extractor["feature_schema_id"]
|
|
757
|
+
or extractor.feature_schema_hash != input_extractor["feature_schema_sha256"]
|
|
758
|
+
):
|
|
759
|
+
raise ValidationError("selector result assembler changed the frozen feature extractor")
|
|
760
|
+
|
|
761
|
+
rank_one = _campaign_rank_one_releases(manifest)
|
|
762
|
+
outcomes, sources = _verified_group_outcomes(
|
|
763
|
+
run_dirs,
|
|
764
|
+
inputs=inputs,
|
|
765
|
+
extractor=extractor,
|
|
766
|
+
utility_definition=utility_definition,
|
|
767
|
+
rank_one=rank_one,
|
|
768
|
+
)
|
|
769
|
+
if sources != tuple(inputs["source_runs"]):
|
|
770
|
+
raise ValidationError("selector result source runs differ from pre-action input")
|
|
771
|
+
groups = {group["group_id"]: group for group in inputs["groups"]}
|
|
772
|
+
result_rows: list[dict[str, Any]] = []
|
|
773
|
+
for decision in frozen_decisions["records"]:
|
|
774
|
+
group = groups[decision["group_id"]]
|
|
775
|
+
family = SemanticFamily(decision["selected_family"])
|
|
776
|
+
selected = outcomes[decision["group_id"]][family]
|
|
777
|
+
baseline = outcomes[decision["group_id"]][SemanticFamily.NO_OP]
|
|
778
|
+
intervened = family is not SemanticFamily.NO_OP
|
|
779
|
+
result_rows.append(
|
|
780
|
+
_campaign_result_row(
|
|
781
|
+
group=group,
|
|
782
|
+
policy_id=decision["policy_id"],
|
|
783
|
+
family=family,
|
|
784
|
+
release_id=decision["selected_release_id"],
|
|
785
|
+
value=selected["value"],
|
|
786
|
+
no_op_value=baseline["value"],
|
|
787
|
+
cost=selected["cost"],
|
|
788
|
+
diagnostic_only=decision["diagnostic_only"],
|
|
789
|
+
hard_harm=intervened and bool(selected["hard_harm"]),
|
|
790
|
+
)
|
|
791
|
+
)
|
|
792
|
+
oracle_policy = next(
|
|
793
|
+
policy for policy in manifest["policies"] if policy["kind"] == "observed_action_oracle"
|
|
794
|
+
)
|
|
795
|
+
for group_id, group in groups.items():
|
|
796
|
+
available = outcomes[group_id]
|
|
797
|
+
selected_family = min(
|
|
798
|
+
available,
|
|
799
|
+
key=lambda family: (
|
|
800
|
+
-available[family]["value"],
|
|
801
|
+
0 if family is SemanticFamily.NO_OP else 1,
|
|
802
|
+
family.value,
|
|
803
|
+
),
|
|
804
|
+
)
|
|
805
|
+
selected = available[selected_family]
|
|
806
|
+
baseline = available[SemanticFamily.NO_OP]
|
|
807
|
+
result_rows.append(
|
|
808
|
+
_campaign_result_row(
|
|
809
|
+
group=group,
|
|
810
|
+
policy_id=oracle_policy["policy_id"],
|
|
811
|
+
family=selected_family,
|
|
812
|
+
release_id=selected["release_id"],
|
|
813
|
+
value=selected["value"],
|
|
814
|
+
no_op_value=baseline["value"],
|
|
815
|
+
cost=selected["cost"],
|
|
816
|
+
diagnostic_only=True,
|
|
817
|
+
hard_harm=(
|
|
818
|
+
selected_family is not SemanticFamily.NO_OP and bool(selected["hard_harm"])
|
|
819
|
+
),
|
|
820
|
+
)
|
|
821
|
+
)
|
|
822
|
+
result = {
|
|
823
|
+
"schema_version": 1,
|
|
824
|
+
"artifact": "agentinputlab.selector_campaign_results",
|
|
825
|
+
"stage": "verified",
|
|
826
|
+
"data_status": "VERIFIED_REAL_RESULTS",
|
|
827
|
+
"is_synthetic": False,
|
|
828
|
+
"campaign_manifest_hash": manifest["campaign_hash"],
|
|
829
|
+
"source_runs": list(sources),
|
|
830
|
+
"records": result_rows,
|
|
831
|
+
}
|
|
832
|
+
return validate_selector_campaign_results(manifest, result)
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def _verified_group_outcomes(
|
|
836
|
+
run_dirs: Sequence[str | Path],
|
|
837
|
+
*,
|
|
838
|
+
inputs: Mapping[str, Any],
|
|
839
|
+
extractor: VisibleStateFeatureExtractor,
|
|
840
|
+
utility_definition: UtilityDefinition,
|
|
841
|
+
rank_one: Mapping[SemanticFamily, str],
|
|
842
|
+
) -> tuple[dict[str, dict[SemanticFamily, dict[str, Any]]], tuple[Mapping[str, Any], ...]]:
|
|
843
|
+
expected_groups = {group["group_id"]: group for group in inputs["groups"]}
|
|
844
|
+
cell_outcomes: dict[str, dict[SemanticFamily, list[EvaluatedArmOutcome]]] = defaultdict(
|
|
845
|
+
lambda: defaultdict(list)
|
|
846
|
+
)
|
|
847
|
+
sources: list[dict[str, str]] = []
|
|
848
|
+
for root_value in run_dirs:
|
|
849
|
+
root = Path(root_value).expanduser().resolve()
|
|
850
|
+
manifest, records, source = _verified_source(root)
|
|
851
|
+
common_support = _validate_evaluation_manifest(
|
|
852
|
+
manifest,
|
|
853
|
+
representative_manifest_hash=inputs["representative_manifest_hash"],
|
|
854
|
+
rank_one=rank_one,
|
|
855
|
+
expected_partial_plan_hash=inputs["partial_support_plan_hash"],
|
|
856
|
+
)
|
|
857
|
+
sources.append(source)
|
|
858
|
+
arm_by_id = {arm.arm_id: arm for arm in manifest.arms}
|
|
859
|
+
no_op_ids = [arm.arm_id for arm in manifest.arms if arm.family is SemanticFamily.NO_OP]
|
|
860
|
+
if len(no_op_ids) != 1:
|
|
861
|
+
raise ValidationError("selector source manifest requires one NO_OP arm")
|
|
862
|
+
representative_arm_ids = {
|
|
863
|
+
arm.family: arm.arm_id
|
|
864
|
+
for arm in manifest.arms
|
|
865
|
+
if arm.family is not SemanticFamily.NO_OP
|
|
866
|
+
}
|
|
867
|
+
for record in records:
|
|
868
|
+
converted, observation, _profile, _model, _backend = _convert_result_record(
|
|
869
|
+
record,
|
|
870
|
+
arm_by_id=arm_by_id,
|
|
871
|
+
no_op_arm_id=no_op_ids[0],
|
|
872
|
+
representative_arm_ids=representative_arm_ids,
|
|
873
|
+
representative_by_family=rank_one,
|
|
874
|
+
extractor=extractor,
|
|
875
|
+
selector_split_override="test",
|
|
876
|
+
)
|
|
877
|
+
group_id = converted[0].group_id
|
|
878
|
+
group = expected_groups.get(group_id)
|
|
879
|
+
if group is None:
|
|
880
|
+
raise ValidationError("selector outcomes contain an unknown group")
|
|
881
|
+
if (
|
|
882
|
+
observation.feature_hash != group["observation"]["feature_hash"]
|
|
883
|
+
or common_support is not group["common_support"]
|
|
884
|
+
):
|
|
885
|
+
raise ValidationError("selector outcomes differ from the frozen pre-action input")
|
|
886
|
+
for outcome in converted:
|
|
887
|
+
cell_outcomes[group_id][outcome.family].append(outcome)
|
|
888
|
+
if set(cell_outcomes) != set(expected_groups):
|
|
889
|
+
raise ValidationError("selector outcomes do not cover every frozen group")
|
|
890
|
+
aggregated: dict[str, dict[SemanticFamily, dict[str, Any]]] = {}
|
|
891
|
+
for group_id, by_family in cell_outcomes.items():
|
|
892
|
+
eligible = {
|
|
893
|
+
SemanticFamily(value) for value in expected_groups[group_id]["eligible_families"]
|
|
894
|
+
}
|
|
895
|
+
expected_actions = {SemanticFamily.NO_OP, *eligible}
|
|
896
|
+
if set(by_family) != expected_actions:
|
|
897
|
+
raise ValidationError("selector outcomes differ from the frozen group eligibility mask")
|
|
898
|
+
aggregated[group_id] = {}
|
|
899
|
+
for family, rows in by_family.items():
|
|
900
|
+
if len(rows) != 2:
|
|
901
|
+
raise ValidationError(
|
|
902
|
+
"selector outcome action requires exactly two model-slot observations"
|
|
903
|
+
)
|
|
904
|
+
values = [utility_definition.value(row) for row in rows]
|
|
905
|
+
release_ids = {row.release_id for row in rows}
|
|
906
|
+
if len(release_ids) != 1:
|
|
907
|
+
raise ValidationError("selector outcome action changed release identity")
|
|
908
|
+
aggregated[group_id][family] = {
|
|
909
|
+
"value": fmean(values),
|
|
910
|
+
"cost": fmean(row.cost for row in rows),
|
|
911
|
+
"hard_harm": any(row.risk > 0.0 for row in rows),
|
|
912
|
+
"release_id": next(iter(release_ids)),
|
|
913
|
+
}
|
|
914
|
+
return aggregated, tuple(sorted(sources, key=lambda item: item["run_identity_hash"]))
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def _pre_action_record(
|
|
918
|
+
record: Mapping[str, Any],
|
|
919
|
+
*,
|
|
920
|
+
arm_by_id: Mapping[str, Any],
|
|
921
|
+
extractor: VisibleStateFeatureExtractor,
|
|
922
|
+
common_support: bool,
|
|
923
|
+
) -> dict[str, Any]:
|
|
924
|
+
cell = _mapping(record.get("cell"), label="selector source cell")
|
|
925
|
+
result = _mapping(record.get("result"), label="selector source result")
|
|
926
|
+
if result.get("status") != "succeeded":
|
|
927
|
+
raise ArtifactError("selector evaluation requires succeeded source cells")
|
|
928
|
+
payload = _mapping(result.get("payload"), label="selector source payload")
|
|
929
|
+
selector_record = _strict_mapping(
|
|
930
|
+
payload.get("selector_record"),
|
|
931
|
+
{"schema_version", "state", "group_id", "eligible_families"},
|
|
932
|
+
label="selector source pre-action record",
|
|
933
|
+
)
|
|
934
|
+
if selector_record["schema_version"] != 1:
|
|
935
|
+
raise ArtifactError("unsupported selector pre-action record schema")
|
|
936
|
+
state = _state_from_artifact(selector_record["state"])
|
|
937
|
+
observation = extractor.extract(state)
|
|
938
|
+
_validate_pre_action_receipt(
|
|
939
|
+
payload.get("pre_action"),
|
|
940
|
+
observation=observation,
|
|
941
|
+
extractor=extractor,
|
|
942
|
+
)
|
|
943
|
+
cell_arm_ids = tuple(
|
|
944
|
+
_text(item, label="selector source arm ID")
|
|
945
|
+
for item in _sequence(cell.get("arm_ids"), label="selector source arm IDs")
|
|
946
|
+
)
|
|
947
|
+
if set(cell_arm_ids) != set(arm_by_id):
|
|
948
|
+
raise ValidationError("selector source cell arms differ from its manifest")
|
|
949
|
+
observed_families = {
|
|
950
|
+
arm_by_id[arm_id].family
|
|
951
|
+
for arm_id in cell_arm_ids
|
|
952
|
+
if arm_by_id[arm_id].family is not SemanticFamily.NO_OP
|
|
953
|
+
}
|
|
954
|
+
eligible = set(_family_sequence(selector_record["eligible_families"]))
|
|
955
|
+
if eligible != observed_families:
|
|
956
|
+
raise ValidationError("selector pre-action eligibility differs from the executed arm block")
|
|
957
|
+
if len(eligible) != (5 if common_support else 3):
|
|
958
|
+
raise ValidationError("selector source support cardinality is invalid")
|
|
959
|
+
benchmark_id = _text(cell.get("benchmark_id"), label="selector source benchmark")
|
|
960
|
+
raw_group_id = _text(selector_record["group_id"], label="selector source group ID")
|
|
961
|
+
group_id = "group_" + content_hash({"benchmark_id": benchmark_id, "group_id": raw_group_id})
|
|
962
|
+
return {
|
|
963
|
+
"group_id": group_id,
|
|
964
|
+
"suite_id": benchmark_id,
|
|
965
|
+
"state_hash": state.state_hash,
|
|
966
|
+
"observation": _observation_to_artifact(observation),
|
|
967
|
+
"eligible_families": [family.value for family in _CORE_FAMILIES if family in eligible],
|
|
968
|
+
"model_slot": _text(cell.get("model_slot"), label="selector source model slot"),
|
|
969
|
+
"seed": _integer(cell.get("seed"), label="selector source seed"),
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
def _verified_source(
|
|
974
|
+
root: Path,
|
|
975
|
+
) -> tuple[ExperimentManifest, tuple[Mapping[str, Any], ...], dict[str, str]]:
|
|
976
|
+
verification = verify_experiment_run_directory(root)
|
|
977
|
+
if not verification["complete"] or not verification["all_succeeded"]:
|
|
978
|
+
raise ArtifactError("selector evaluation requires complete, all-succeeded verified runs")
|
|
979
|
+
manifest = load_experiment_manifest(root / "manifest.json")
|
|
980
|
+
records = load_verified_cell_results(root)
|
|
981
|
+
source = {
|
|
982
|
+
"study_id": manifest.study_id,
|
|
983
|
+
"run_identity_hash": _sha256(verification["identity_hash"], label="source run identity"),
|
|
984
|
+
"manifest_hash": _sha256(verification["manifest_hash"], label="source manifest hash"),
|
|
985
|
+
"plan_hash": _sha256(verification["plan_hash"], label="source plan hash"),
|
|
986
|
+
"result_set_hash": content_hash(records),
|
|
987
|
+
}
|
|
988
|
+
return manifest, records, source
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
def _validate_evaluation_manifest(
|
|
992
|
+
manifest: ExperimentManifest,
|
|
993
|
+
*,
|
|
994
|
+
representative_manifest_hash: str,
|
|
995
|
+
rank_one: Mapping[SemanticFamily, str],
|
|
996
|
+
expected_partial_plan_hash: str,
|
|
997
|
+
) -> bool:
|
|
998
|
+
common_support = manifest.study_id == "primary.common_support"
|
|
999
|
+
partial_support = manifest.study_id.startswith("selector.partial_support.")
|
|
1000
|
+
if not common_support and not partial_support:
|
|
1001
|
+
raise ValidationError("selector evaluation received an undeclared source study")
|
|
1002
|
+
if common_support:
|
|
1003
|
+
census = manifest.metadata.get("experiment_census", {})
|
|
1004
|
+
if (
|
|
1005
|
+
manifest.stage is not ExperimentStage.CONFIRMATORY
|
|
1006
|
+
or census.get("campaign_kind") != "common_support"
|
|
1007
|
+
or census.get("independent_group_target") != 526
|
|
1008
|
+
or census.get("expected_agent_episodes") != 6312
|
|
1009
|
+
):
|
|
1010
|
+
raise ValidationError("selector common-support source is not confirmatory")
|
|
1011
|
+
expected_count = 5
|
|
1012
|
+
else:
|
|
1013
|
+
census = manifest.metadata.get("experiment_census", {})
|
|
1014
|
+
if (
|
|
1015
|
+
manifest.stage is not ExperimentStage.SELECTOR_CONFIRMATION
|
|
1016
|
+
or manifest.metadata.get("partial_support") is not True
|
|
1017
|
+
or manifest.metadata.get("common_support") is not False
|
|
1018
|
+
or manifest.metadata.get("fit_or_calibrate_allowed") is not False
|
|
1019
|
+
or manifest.metadata.get("test_outcomes_visible_to_selector") is not False
|
|
1020
|
+
or manifest.metadata.get("eligibility_plan_hash") != expected_partial_plan_hash
|
|
1021
|
+
or census.get("campaign_kind") != "selector_partial_support"
|
|
1022
|
+
or census.get("independent_group_target") != 10
|
|
1023
|
+
or census.get("expected_agent_episodes") != 80
|
|
1024
|
+
):
|
|
1025
|
+
raise ValidationError("selector partial-support source is not frozen")
|
|
1026
|
+
expected_count = 3
|
|
1027
|
+
if (
|
|
1028
|
+
manifest.metadata.get("representative_manifest_hash") != representative_manifest_hash
|
|
1029
|
+
or manifest.model_slots != ("anchor_a", "anchor_b")
|
|
1030
|
+
or manifest.seeds != (17,)
|
|
1031
|
+
):
|
|
1032
|
+
raise ValidationError("selector source changed representatives, models, or seed")
|
|
1033
|
+
no_ops = [arm for arm in manifest.arms if arm.family is SemanticFamily.NO_OP]
|
|
1034
|
+
interventions = [arm for arm in manifest.arms if arm.family is not SemanticFamily.NO_OP]
|
|
1035
|
+
if (
|
|
1036
|
+
len(no_ops) != 1
|
|
1037
|
+
or no_ops[0].release_id != "baseline.no_op"
|
|
1038
|
+
or len(interventions) != expected_count
|
|
1039
|
+
or len({arm.family for arm in interventions}) != expected_count
|
|
1040
|
+
):
|
|
1041
|
+
raise ValidationError("selector source arm support is invalid")
|
|
1042
|
+
for arm in interventions:
|
|
1043
|
+
if (
|
|
1044
|
+
arm.release_id != rank_one.get(arm.family)
|
|
1045
|
+
or arm.parameters.get("representative_rank") != 1
|
|
1046
|
+
or arm.parameters.get("representative_manifest_hash") != representative_manifest_hash
|
|
1047
|
+
):
|
|
1048
|
+
raise ValidationError("selector source arm is not a rank-1 representative")
|
|
1049
|
+
return common_support
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def _select_policy_action(
|
|
1053
|
+
policy: Mapping[str, Any],
|
|
1054
|
+
*,
|
|
1055
|
+
group: Mapping[str, Any],
|
|
1056
|
+
state: FrozenAgentState,
|
|
1057
|
+
features: Any,
|
|
1058
|
+
eligible: Sequence[SemanticFamily],
|
|
1059
|
+
rank_one: Mapping[SemanticFamily, str],
|
|
1060
|
+
loaded: Mapping[str, Any],
|
|
1061
|
+
random_seed: int,
|
|
1062
|
+
) -> tuple[SemanticFamily, str]:
|
|
1063
|
+
kind = policy["kind"]
|
|
1064
|
+
policy_id = policy["policy_id"]
|
|
1065
|
+
parameters = policy["parameters"]
|
|
1066
|
+
if kind == "no_op":
|
|
1067
|
+
return SemanticFamily.NO_OP, "baseline.no_op"
|
|
1068
|
+
if kind == "random_eligible":
|
|
1069
|
+
actions = (SemanticFamily.NO_OP, *eligible)
|
|
1070
|
+
seed = int(
|
|
1071
|
+
content_hash(
|
|
1072
|
+
{
|
|
1073
|
+
"campaign_seed": random_seed,
|
|
1074
|
+
"group_id": group["group_id"],
|
|
1075
|
+
"policy_id": policy_id,
|
|
1076
|
+
}
|
|
1077
|
+
),
|
|
1078
|
+
16,
|
|
1079
|
+
)
|
|
1080
|
+
family = random.Random(seed).choice(actions)
|
|
1081
|
+
return _release_for(family, rank_one)
|
|
1082
|
+
if kind == "fixed_family":
|
|
1083
|
+
family = SemanticFamily(parameters["family"])
|
|
1084
|
+
return (
|
|
1085
|
+
_release_for(family, rank_one)
|
|
1086
|
+
if family in eligible
|
|
1087
|
+
else (SemanticFamily.NO_OP, "baseline.no_op")
|
|
1088
|
+
)
|
|
1089
|
+
if kind == "best_fixed_global":
|
|
1090
|
+
family = SemanticFamily(parameters["selected_family"])
|
|
1091
|
+
return (
|
|
1092
|
+
_release_for(family, rank_one)
|
|
1093
|
+
if family in eligible
|
|
1094
|
+
else (SemanticFamily.NO_OP, "baseline.no_op")
|
|
1095
|
+
)
|
|
1096
|
+
if kind != "frozen_artifact_policy":
|
|
1097
|
+
raise ValidationError(f"unsupported outcome-blind selector policy kind: {kind}")
|
|
1098
|
+
artifact = loaded[policy_id]
|
|
1099
|
+
if isinstance(artifact, Mapping):
|
|
1100
|
+
replay = artifact[group["group_id"]]
|
|
1101
|
+
return SemanticFamily(replay["selected_family"]), replay["selected_release_id"]
|
|
1102
|
+
decision = artifact.select(
|
|
1103
|
+
state,
|
|
1104
|
+
features,
|
|
1105
|
+
eligible_families=eligible,
|
|
1106
|
+
)
|
|
1107
|
+
if decision.family is SemanticFamily.NO_OP:
|
|
1108
|
+
return SemanticFamily.NO_OP, "baseline.no_op"
|
|
1109
|
+
if decision.family not in eligible:
|
|
1110
|
+
raise ValidationError(f"frozen selector {policy_id} selected an ineligible family")
|
|
1111
|
+
expected_release = rank_one[decision.family]
|
|
1112
|
+
if decision.release_id not in (None, expected_release):
|
|
1113
|
+
raise ValidationError(f"frozen selector {policy_id} changed representative release")
|
|
1114
|
+
return decision.family, expected_release
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
def _campaign_result_row(
|
|
1118
|
+
*,
|
|
1119
|
+
group: Mapping[str, Any],
|
|
1120
|
+
policy_id: str,
|
|
1121
|
+
family: SemanticFamily,
|
|
1122
|
+
release_id: str,
|
|
1123
|
+
value: float,
|
|
1124
|
+
no_op_value: float,
|
|
1125
|
+
cost: float,
|
|
1126
|
+
diagnostic_only: bool,
|
|
1127
|
+
hard_harm: bool,
|
|
1128
|
+
) -> dict[str, Any]:
|
|
1129
|
+
return {
|
|
1130
|
+
"group_id": group["group_id"],
|
|
1131
|
+
"suite_id": group["suite_id"],
|
|
1132
|
+
"policy_id": policy_id,
|
|
1133
|
+
"selected_family": family.value,
|
|
1134
|
+
"selected_release_id": release_id,
|
|
1135
|
+
"value": value,
|
|
1136
|
+
"no_op_value": no_op_value,
|
|
1137
|
+
"hard_harm": hard_harm,
|
|
1138
|
+
"cost": cost,
|
|
1139
|
+
"intervened": family is not SemanticFamily.NO_OP,
|
|
1140
|
+
"selection_eligible": True,
|
|
1141
|
+
"common_support": group["common_support"],
|
|
1142
|
+
"diagnostic_only": diagnostic_only,
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
def _validate_campaign_input_binding(
|
|
1147
|
+
campaign: Mapping[str, Any],
|
|
1148
|
+
inputs: Mapping[str, Any],
|
|
1149
|
+
) -> None:
|
|
1150
|
+
design = inputs["campaign_design"]
|
|
1151
|
+
if (
|
|
1152
|
+
campaign["campaign_id"] != design["campaign_id"]
|
|
1153
|
+
or campaign["primary_metric"] != design["primary_metric"]
|
|
1154
|
+
or campaign["evaluation_split"] != design["evaluation_split"]
|
|
1155
|
+
or campaign["representative_manifest_hash"] != inputs["representative_manifest_hash"]
|
|
1156
|
+
or campaign["metadata"]["suite_ids"] != design["suite_ids"]
|
|
1157
|
+
or campaign["metadata"]["required_independent_groups"]
|
|
1158
|
+
!= design["required_independent_groups"]
|
|
1159
|
+
or campaign["metadata"]["minimum_common_support_groups"]
|
|
1160
|
+
!= design["minimum_common_support_groups"]
|
|
1161
|
+
or campaign["metadata"]["random_seed"] != design["random_seed"]
|
|
1162
|
+
):
|
|
1163
|
+
raise ValidationError("selector campaign differs from its pre-action input design")
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
def _campaign_rank_one_releases(
|
|
1167
|
+
campaign: Mapping[str, Any],
|
|
1168
|
+
) -> dict[SemanticFamily, str]:
|
|
1169
|
+
policies = {policy["policy_id"]: policy for policy in campaign["policies"]}
|
|
1170
|
+
releases = {
|
|
1171
|
+
family: policies[f"fixed_{family.value}"]["parameters"]["release_id"]
|
|
1172
|
+
for family in _CORE_FAMILIES
|
|
1173
|
+
}
|
|
1174
|
+
if any(not isinstance(value, str) or not value for value in releases.values()):
|
|
1175
|
+
raise ValidationError("selector campaign fixed-family releases are invalid")
|
|
1176
|
+
return releases
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
def _rank_one_releases(
|
|
1180
|
+
representatives: Mapping[str, Any],
|
|
1181
|
+
) -> dict[SemanticFamily, str]:
|
|
1182
|
+
result = {
|
|
1183
|
+
SemanticFamily(row["family"]): row["release_id"]
|
|
1184
|
+
for row in representatives["selections"]
|
|
1185
|
+
if row["rank"] == 1
|
|
1186
|
+
}
|
|
1187
|
+
if set(result) != set(_CORE_FAMILIES):
|
|
1188
|
+
raise ValidationError("selector evaluation requires one rank-1 release per family")
|
|
1189
|
+
return result
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
def _validate_selected_action(
|
|
1193
|
+
*,
|
|
1194
|
+
group: Mapping[str, Any],
|
|
1195
|
+
family: SemanticFamily,
|
|
1196
|
+
release_id: str,
|
|
1197
|
+
) -> None:
|
|
1198
|
+
if family is SemanticFamily.NO_OP:
|
|
1199
|
+
if release_id != "baseline.no_op":
|
|
1200
|
+
raise ValidationError("selector NO_OP decision requires baseline.no_op")
|
|
1201
|
+
return
|
|
1202
|
+
if family.value not in group["eligible_families"]:
|
|
1203
|
+
raise ValidationError("selector decision selected a structurally ineligible family")
|
|
1204
|
+
if release_id == "baseline.no_op":
|
|
1205
|
+
raise ValidationError("selector intervention decision requires an intervention release")
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
def _release_for(
|
|
1209
|
+
family: SemanticFamily,
|
|
1210
|
+
rank_one: Mapping[SemanticFamily, str],
|
|
1211
|
+
) -> tuple[SemanticFamily, str]:
|
|
1212
|
+
if family is SemanticFamily.NO_OP:
|
|
1213
|
+
return family, "baseline.no_op"
|
|
1214
|
+
return family, rank_one[family]
|
|
1215
|
+
|
|
1216
|
+
|
|
1217
|
+
def _validate_sources(value: Any) -> tuple[dict[str, str], ...]:
|
|
1218
|
+
rows = _sequence(value, label="selector evaluation source runs")
|
|
1219
|
+
parsed: list[dict[str, str]] = []
|
|
1220
|
+
identities: set[str] = set()
|
|
1221
|
+
for index, raw in enumerate(rows):
|
|
1222
|
+
row = _strict_mapping(
|
|
1223
|
+
raw,
|
|
1224
|
+
{
|
|
1225
|
+
"study_id",
|
|
1226
|
+
"run_identity_hash",
|
|
1227
|
+
"manifest_hash",
|
|
1228
|
+
"plan_hash",
|
|
1229
|
+
"result_set_hash",
|
|
1230
|
+
},
|
|
1231
|
+
label=f"selector evaluation source {index}",
|
|
1232
|
+
)
|
|
1233
|
+
parsed_row = {
|
|
1234
|
+
"study_id": _text(row["study_id"], label="selector source study"),
|
|
1235
|
+
"run_identity_hash": _sha256(
|
|
1236
|
+
row["run_identity_hash"], label="selector source identity"
|
|
1237
|
+
),
|
|
1238
|
+
"manifest_hash": _sha256(row["manifest_hash"], label="selector source manifest"),
|
|
1239
|
+
"plan_hash": _sha256(row["plan_hash"], label="selector source plan"),
|
|
1240
|
+
"result_set_hash": _sha256(row["result_set_hash"], label="selector source results"),
|
|
1241
|
+
}
|
|
1242
|
+
if parsed_row["run_identity_hash"] in identities:
|
|
1243
|
+
raise ValidationError("selector source run identities must be unique")
|
|
1244
|
+
identities.add(parsed_row["run_identity_hash"])
|
|
1245
|
+
parsed.append(parsed_row)
|
|
1246
|
+
return tuple(sorted(parsed, key=lambda item: item["run_identity_hash"]))
|
|
1247
|
+
|
|
1248
|
+
|
|
1249
|
+
def _jsonable_mapping(value: Mapping[str, Any]) -> dict[str, Any]:
|
|
1250
|
+
try:
|
|
1251
|
+
normalized = to_jsonable(value)
|
|
1252
|
+
except (TypeError, ValueError) as exc:
|
|
1253
|
+
raise ValidationError("selector artifact mapping must be JSON serializable") from exc
|
|
1254
|
+
if not isinstance(normalized, dict):
|
|
1255
|
+
raise ValidationError("selector artifact mapping must remain an object")
|
|
1256
|
+
return normalized
|
|
1257
|
+
|
|
1258
|
+
|
|
1259
|
+
def _family_sequence(value: Any) -> tuple[SemanticFamily, ...]:
|
|
1260
|
+
raw = _sequence(value, label="eligible families")
|
|
1261
|
+
try:
|
|
1262
|
+
families = tuple(SemanticFamily(item) for item in raw)
|
|
1263
|
+
except (TypeError, ValueError) as exc:
|
|
1264
|
+
raise ValidationError("eligible families contain an unknown semantic family") from exc
|
|
1265
|
+
if (
|
|
1266
|
+
len(families) != len(set(families))
|
|
1267
|
+
or any(family not in _CORE_FAMILIES for family in families)
|
|
1268
|
+
or families != tuple(family for family in _CORE_FAMILIES if family in families)
|
|
1269
|
+
):
|
|
1270
|
+
raise ValidationError("eligible families must be canonical unique core families")
|
|
1271
|
+
return families
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
def _family(value: Any) -> SemanticFamily:
|
|
1275
|
+
try:
|
|
1276
|
+
family = SemanticFamily(value)
|
|
1277
|
+
except (TypeError, ValueError) as exc:
|
|
1278
|
+
raise ValidationError("selector decision family is invalid") from exc
|
|
1279
|
+
if family not in {SemanticFamily.NO_OP, *_CORE_FAMILIES}:
|
|
1280
|
+
raise ValidationError("selector decision family is outside the campaign action set")
|
|
1281
|
+
return family
|
|
1282
|
+
|
|
1283
|
+
|
|
1284
|
+
def _strict_mapping(value: Any, keys: set[str], *, label: str) -> dict[str, Any]:
|
|
1285
|
+
if not isinstance(value, Mapping) or any(not isinstance(key, str) for key in value):
|
|
1286
|
+
raise ValidationError(f"{label} must be an object")
|
|
1287
|
+
data = dict(value)
|
|
1288
|
+
if set(data) != keys:
|
|
1289
|
+
raise ValidationError(f"{label} fields differ from the frozen schema")
|
|
1290
|
+
return data
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
def _mapping(value: Any, *, label: str) -> dict[str, Any]:
|
|
1294
|
+
if not isinstance(value, Mapping) or any(not isinstance(key, str) for key in value):
|
|
1295
|
+
raise ValidationError(f"{label} must be an object")
|
|
1296
|
+
return dict(value)
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
def _sequence(value: Any, *, label: str) -> tuple[Any, ...]:
|
|
1300
|
+
if isinstance(value, str | bytes) or not isinstance(value, Sequence):
|
|
1301
|
+
raise ValidationError(f"{label} must be an array")
|
|
1302
|
+
return tuple(value)
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
def _text(value: Any, *, label: str) -> str:
|
|
1306
|
+
if not isinstance(value, str) or not value.strip() or value != value.strip():
|
|
1307
|
+
raise ValidationError(f"{label} must be a trimmed non-empty string")
|
|
1308
|
+
return value
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
def _integer(value: Any, *, label: str) -> int:
|
|
1312
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
1313
|
+
raise ValidationError(f"{label} must be a non-negative integer")
|
|
1314
|
+
return value
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
def _boolean(value: Any, *, label: str) -> bool:
|
|
1318
|
+
if not isinstance(value, bool):
|
|
1319
|
+
raise ValidationError(f"{label} must be boolean")
|
|
1320
|
+
return value
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
def _require_sha256(value: Any, *, label: str) -> None:
|
|
1324
|
+
if not isinstance(value, str) or _SHA256.fullmatch(value) is None:
|
|
1325
|
+
raise ValidationError(f"{label} must be a lowercase SHA-256")
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
def _sha256(value: Any, *, label: str) -> str:
|
|
1329
|
+
_require_sha256(value, label=label)
|
|
1330
|
+
return value
|
|
1331
|
+
|
|
1332
|
+
|
|
1333
|
+
def _group_id(value: Any, *, label: str) -> str:
|
|
1334
|
+
text = _text(value, label=label)
|
|
1335
|
+
if not text.startswith("group_") or _SHA256.fullmatch(text[6:]) is None:
|
|
1336
|
+
raise ValidationError(f"{label} must be a privacy-safe group hash")
|
|
1337
|
+
return text
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
__all__ = [
|
|
1341
|
+
"assemble_selector_campaign_results",
|
|
1342
|
+
"build_selector_campaign_decisions",
|
|
1343
|
+
"build_selector_evaluation_input",
|
|
1344
|
+
"freeze_zero_shot_router_replay",
|
|
1345
|
+
"validate_selector_campaign_decisions",
|
|
1346
|
+
"validate_selector_evaluation_input",
|
|
1347
|
+
"validate_zero_shot_router_replay",
|
|
1348
|
+
]
|