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,675 @@
|
|
|
1
|
+
"""Blinded, outcome-free human measurement preparation and agreement checks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import math
|
|
7
|
+
from collections import Counter, defaultdict
|
|
8
|
+
from collections.abc import Mapping, Sequence
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from agentinputlab.contracts.errors import ValidationError
|
|
13
|
+
from agentinputlab.contracts.serialization import (
|
|
14
|
+
content_hash,
|
|
15
|
+
freeze_mapping,
|
|
16
|
+
to_jsonable,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
MEASUREMENT_ASSIGNMENT_PROTOCOL = "agentinputlab.measurement-assignment.v1"
|
|
20
|
+
MEASUREMENT_ANALYSIS_PROTOCOL = "agentinputlab.measurement-analysis.v1"
|
|
21
|
+
AUTOMATIC_HUMAN_VALIDATION_PROTOCOL = "agentinputlab.automatic-human-validation.v1"
|
|
22
|
+
MEASUREMENT_KINDS = ("contract_anchor", "defect_eligibility")
|
|
23
|
+
DEFAULT_MEASUREMENT_COUNTS = {
|
|
24
|
+
"contract_anchor": 200,
|
|
25
|
+
"defect_eligibility": 100,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_FORBIDDEN_VISIBLE_KEYS = frozenset(
|
|
29
|
+
{
|
|
30
|
+
"answer",
|
|
31
|
+
"arm",
|
|
32
|
+
"arm_id",
|
|
33
|
+
"gold",
|
|
34
|
+
"gold_answer",
|
|
35
|
+
"intervention",
|
|
36
|
+
"outcome",
|
|
37
|
+
"post_action",
|
|
38
|
+
"reference_answer",
|
|
39
|
+
"release_id",
|
|
40
|
+
"reward",
|
|
41
|
+
"score",
|
|
42
|
+
"success",
|
|
43
|
+
"task_success",
|
|
44
|
+
"treatment",
|
|
45
|
+
"valid_success",
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _text(value: Any, *, label: str) -> str:
|
|
51
|
+
if not isinstance(value, str) or not value.strip() or value != value.strip():
|
|
52
|
+
raise ValidationError(f"{label} must be a trimmed non-empty string")
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _validate_outcome_free(value: Any, *, path: str = "payload") -> None:
|
|
57
|
+
if isinstance(value, Mapping):
|
|
58
|
+
for key, item in value.items():
|
|
59
|
+
if not isinstance(key, str) or not key:
|
|
60
|
+
raise ValidationError(f"{path} keys must be non-empty strings")
|
|
61
|
+
normalized = key.casefold().replace("-", "_")
|
|
62
|
+
if normalized in _FORBIDDEN_VISIBLE_KEYS:
|
|
63
|
+
raise ValidationError(f"{path}.{key} exposes a forbidden outcome/treatment field")
|
|
64
|
+
_validate_outcome_free(item, path=f"{path}.{key}")
|
|
65
|
+
elif isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
|
|
66
|
+
for index, item in enumerate(value):
|
|
67
|
+
_validate_outcome_free(item, path=f"{path}[{index}]")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True, slots=True)
|
|
71
|
+
class MeasurementItem:
|
|
72
|
+
item_id: str
|
|
73
|
+
group_id: str
|
|
74
|
+
kind: str
|
|
75
|
+
payload: Mapping[str, Any] = field(repr=False)
|
|
76
|
+
|
|
77
|
+
def __post_init__(self) -> None:
|
|
78
|
+
_text(self.item_id, label="measurement item_id")
|
|
79
|
+
_text(self.group_id, label="measurement group_id")
|
|
80
|
+
if self.kind not in MEASUREMENT_KINDS:
|
|
81
|
+
raise ValidationError("measurement kind must be contract_anchor or defect_eligibility")
|
|
82
|
+
if not isinstance(self.payload, Mapping) or not self.payload:
|
|
83
|
+
raise ValidationError("measurement payload must be a non-empty object")
|
|
84
|
+
_validate_outcome_free(self.payload)
|
|
85
|
+
try:
|
|
86
|
+
payload = freeze_mapping(self.payload)
|
|
87
|
+
except (TypeError, ValueError) as exc:
|
|
88
|
+
raise ValidationError("measurement payload must be canonical JSON") from exc
|
|
89
|
+
object.__setattr__(self, "payload", payload)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def blind_id(self) -> str:
|
|
93
|
+
return "blind_" + content_hash(
|
|
94
|
+
{
|
|
95
|
+
"item_id": self.item_id,
|
|
96
|
+
"group_id": self.group_id,
|
|
97
|
+
"kind": self.kind,
|
|
98
|
+
"payload": self.payload,
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def blinded_record(self) -> dict[str, Any]:
|
|
103
|
+
return {
|
|
104
|
+
"blind_id": self.blind_id,
|
|
105
|
+
"group_hash": "group_" + content_hash({"group_id": self.group_id}),
|
|
106
|
+
"kind": self.kind,
|
|
107
|
+
"payload": to_jsonable(self.payload),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _annotators(values: Sequence[str]) -> tuple[str, ...]:
|
|
112
|
+
annotators = tuple(_text(value, label="annotator ID") for value in values)
|
|
113
|
+
if len(annotators) < 3:
|
|
114
|
+
raise ValidationError(
|
|
115
|
+
"measurement requires at least three annotators for two-pass review and adjudication"
|
|
116
|
+
)
|
|
117
|
+
if len(annotators) != len(set(annotators)):
|
|
118
|
+
raise ValidationError("annotator IDs must be unique")
|
|
119
|
+
return annotators
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def build_measurement_assignment(
|
|
123
|
+
items: Sequence[MeasurementItem],
|
|
124
|
+
*,
|
|
125
|
+
annotator_ids: Sequence[str],
|
|
126
|
+
seed: int,
|
|
127
|
+
expected_counts: Mapping[str, int] | None = None,
|
|
128
|
+
allow_partial: bool = False,
|
|
129
|
+
) -> dict[str, Any]:
|
|
130
|
+
"""Create deterministic, balanced, double-blind first-pass packets."""
|
|
131
|
+
|
|
132
|
+
if isinstance(seed, bool) or not isinstance(seed, int) or seed < 0:
|
|
133
|
+
raise ValidationError("measurement seed must be a non-negative integer")
|
|
134
|
+
if not isinstance(allow_partial, bool):
|
|
135
|
+
raise ValidationError("allow_partial must be boolean")
|
|
136
|
+
values = tuple(items)
|
|
137
|
+
if not values or not all(isinstance(item, MeasurementItem) for item in values):
|
|
138
|
+
raise ValidationError("measurement assignment requires typed items")
|
|
139
|
+
if len({item.item_id for item in values}) != len(values):
|
|
140
|
+
raise ValidationError("measurement item IDs must be unique")
|
|
141
|
+
if len({item.blind_id for item in values}) != len(values):
|
|
142
|
+
raise ValidationError("measurement blind IDs must be unique")
|
|
143
|
+
annotators = _annotators(annotator_ids)
|
|
144
|
+
counts = Counter(item.kind for item in values)
|
|
145
|
+
expected = dict(DEFAULT_MEASUREMENT_COUNTS if expected_counts is None else expected_counts)
|
|
146
|
+
if set(expected) != set(MEASUREMENT_KINDS) or any(
|
|
147
|
+
isinstance(count, bool) or not isinstance(count, int) or count < 1
|
|
148
|
+
for count in expected.values()
|
|
149
|
+
):
|
|
150
|
+
raise ValidationError("measurement expected_counts are invalid")
|
|
151
|
+
if not allow_partial and dict(counts) != expected:
|
|
152
|
+
raise ValidationError(
|
|
153
|
+
f"measurement item counts differ from the frozen census: "
|
|
154
|
+
f"observed={dict(counts)}, expected={expected}"
|
|
155
|
+
)
|
|
156
|
+
if allow_partial and any(counts.get(kind, 0) < 1 for kind in MEASUREMENT_KINDS):
|
|
157
|
+
raise ValidationError("partial measurement smoke still requires both measurement kinds")
|
|
158
|
+
|
|
159
|
+
packets: dict[str, list[dict[str, Any]]] = {annotator: [] for annotator in annotators}
|
|
160
|
+
assignment_rows: list[dict[str, Any]] = []
|
|
161
|
+
for kind in MEASUREMENT_KINDS:
|
|
162
|
+
kind_items = [item for item in values if item.kind == kind]
|
|
163
|
+
ordered = sorted(
|
|
164
|
+
kind_items,
|
|
165
|
+
key=lambda item: hashlib.sha256(f"{seed}:{kind}:{item.blind_id}".encode()).hexdigest(),
|
|
166
|
+
)
|
|
167
|
+
offset = int(
|
|
168
|
+
hashlib.sha256(f"{seed}:{kind}:offset".encode()).hexdigest()[:8],
|
|
169
|
+
16,
|
|
170
|
+
) % len(annotators)
|
|
171
|
+
for position, item in enumerate(ordered):
|
|
172
|
+
first = annotators[(offset + position) % len(annotators)]
|
|
173
|
+
second = annotators[(offset + position + 1) % len(annotators)]
|
|
174
|
+
record = item.blinded_record()
|
|
175
|
+
assignment_rows.append(
|
|
176
|
+
{
|
|
177
|
+
"blind_id": item.blind_id,
|
|
178
|
+
"kind": kind,
|
|
179
|
+
"assigned_annotators": [first, second],
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
for annotator in (first, second):
|
|
183
|
+
packets[annotator].append(
|
|
184
|
+
{
|
|
185
|
+
**record,
|
|
186
|
+
"annotator_id": annotator,
|
|
187
|
+
"annotation_stage": "first_pass",
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
for packet in packets.values():
|
|
191
|
+
packet.sort(key=lambda row: (row["kind"], row["blind_id"]))
|
|
192
|
+
assignment_rows.sort(key=lambda row: (row["kind"], row["blind_id"]))
|
|
193
|
+
packet_payload = {annotator: rows for annotator, rows in sorted(packets.items())}
|
|
194
|
+
manifest = {
|
|
195
|
+
"schema_version": 1,
|
|
196
|
+
"protocol": MEASUREMENT_ASSIGNMENT_PROTOCOL,
|
|
197
|
+
"record_type": "MEASUREMENT_PLAN",
|
|
198
|
+
"observed": False,
|
|
199
|
+
"outcome_blinded": True,
|
|
200
|
+
"seed": seed,
|
|
201
|
+
"expected_counts": expected,
|
|
202
|
+
"observed_counts": dict(sorted(counts.items())),
|
|
203
|
+
"allow_partial": allow_partial,
|
|
204
|
+
"annotator_ids": list(annotators),
|
|
205
|
+
"double_annotation": True,
|
|
206
|
+
"adjudication_policy": "third_annotator_required_on_any_field_disagreement",
|
|
207
|
+
"assignments": assignment_rows,
|
|
208
|
+
"packet_hashes": {
|
|
209
|
+
annotator: content_hash(rows) for annotator, rows in packet_payload.items()
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
manifest["assignment_hash"] = content_hash(manifest)
|
|
213
|
+
return {
|
|
214
|
+
"manifest": manifest,
|
|
215
|
+
"packets": packet_payload,
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _label_scalar(value: Any, *, path: str) -> Any:
|
|
220
|
+
if isinstance(value, bool):
|
|
221
|
+
return value
|
|
222
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
223
|
+
return value
|
|
224
|
+
if isinstance(value, str) and value.strip() and value == value.strip():
|
|
225
|
+
return value
|
|
226
|
+
raise ValidationError(f"{path} must be a boolean, integer, or trimmed non-empty string")
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _assignment_manifest(value: Any) -> Mapping[str, Any]:
|
|
230
|
+
if not isinstance(value, Mapping):
|
|
231
|
+
raise ValidationError("measurement assignment manifest must be an object")
|
|
232
|
+
required = {
|
|
233
|
+
"schema_version",
|
|
234
|
+
"protocol",
|
|
235
|
+
"record_type",
|
|
236
|
+
"observed",
|
|
237
|
+
"outcome_blinded",
|
|
238
|
+
"seed",
|
|
239
|
+
"expected_counts",
|
|
240
|
+
"observed_counts",
|
|
241
|
+
"allow_partial",
|
|
242
|
+
"annotator_ids",
|
|
243
|
+
"double_annotation",
|
|
244
|
+
"adjudication_policy",
|
|
245
|
+
"assignments",
|
|
246
|
+
"packet_hashes",
|
|
247
|
+
"assignment_hash",
|
|
248
|
+
}
|
|
249
|
+
if set(value) != required:
|
|
250
|
+
raise ValidationError("measurement assignment manifest fields are invalid")
|
|
251
|
+
if (
|
|
252
|
+
value["schema_version"] != 1
|
|
253
|
+
or value["protocol"] != MEASUREMENT_ASSIGNMENT_PROTOCOL
|
|
254
|
+
or value["record_type"] != "MEASUREMENT_PLAN"
|
|
255
|
+
or value["observed"] is not False
|
|
256
|
+
or value["outcome_blinded"] is not True
|
|
257
|
+
or value["double_annotation"] is not True
|
|
258
|
+
):
|
|
259
|
+
raise ValidationError("measurement assignment manifest identity is invalid")
|
|
260
|
+
unhashed = dict(value)
|
|
261
|
+
observed_hash = unhashed.pop("assignment_hash")
|
|
262
|
+
if observed_hash != content_hash(unhashed):
|
|
263
|
+
raise ValidationError("measurement assignment manifest hash mismatch")
|
|
264
|
+
return freeze_mapping(value)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def analyze_measurement_annotations(
|
|
268
|
+
assignment_manifest: Mapping[str, Any],
|
|
269
|
+
annotations: Sequence[Mapping[str, Any]],
|
|
270
|
+
) -> dict[str, Any]:
|
|
271
|
+
"""Validate assigned first-pass records and compute field-level agreement."""
|
|
272
|
+
|
|
273
|
+
manifest = _assignment_manifest(assignment_manifest)
|
|
274
|
+
assignments: dict[str, tuple[str, tuple[str, str]]] = {}
|
|
275
|
+
for row in manifest["assignments"]:
|
|
276
|
+
if not isinstance(row, Mapping) or set(row) != {
|
|
277
|
+
"blind_id",
|
|
278
|
+
"kind",
|
|
279
|
+
"assigned_annotators",
|
|
280
|
+
}:
|
|
281
|
+
raise ValidationError("measurement assignment row is invalid")
|
|
282
|
+
blind_id = _text(row["blind_id"], label="assignment blind_id")
|
|
283
|
+
kind = row["kind"]
|
|
284
|
+
raw_annotators = row["assigned_annotators"]
|
|
285
|
+
if (
|
|
286
|
+
kind not in MEASUREMENT_KINDS
|
|
287
|
+
or not isinstance(raw_annotators, Sequence)
|
|
288
|
+
or isinstance(raw_annotators, (str, bytes))
|
|
289
|
+
or len(raw_annotators) != 2
|
|
290
|
+
):
|
|
291
|
+
raise ValidationError("measurement assignment row values are invalid")
|
|
292
|
+
assigned = tuple(_text(value, label="assigned annotator") for value in raw_annotators)
|
|
293
|
+
if assigned[0] == assigned[1]:
|
|
294
|
+
raise ValidationError("measurement assignment requires two distinct annotators")
|
|
295
|
+
if blind_id in assignments:
|
|
296
|
+
raise ValidationError("measurement assignment contains duplicate blind IDs")
|
|
297
|
+
assignments[blind_id] = (kind, assigned)
|
|
298
|
+
|
|
299
|
+
first_pass: dict[str, dict[str, Mapping[str, Any]]] = defaultdict(dict)
|
|
300
|
+
adjudications: dict[str, tuple[str, Mapping[str, Any]]] = {}
|
|
301
|
+
for index, raw in enumerate(annotations):
|
|
302
|
+
if not isinstance(raw, Mapping) or set(raw) != {
|
|
303
|
+
"blind_id",
|
|
304
|
+
"annotator_id",
|
|
305
|
+
"annotation_stage",
|
|
306
|
+
"labels",
|
|
307
|
+
}:
|
|
308
|
+
raise ValidationError(f"annotation[{index}] fields are invalid")
|
|
309
|
+
blind_id = _text(raw["blind_id"], label=f"annotation[{index}].blind_id")
|
|
310
|
+
annotator = _text(raw["annotator_id"], label=f"annotation[{index}].annotator_id")
|
|
311
|
+
try:
|
|
312
|
+
_kind, assigned = assignments[blind_id]
|
|
313
|
+
except KeyError as exc:
|
|
314
|
+
raise ValidationError("annotation references an unknown blind ID") from exc
|
|
315
|
+
labels = raw["labels"]
|
|
316
|
+
if not isinstance(labels, Mapping) or not labels:
|
|
317
|
+
raise ValidationError("annotation labels must be a non-empty object")
|
|
318
|
+
normalized = {
|
|
319
|
+
_text(key, label="annotation label field"): _label_scalar(
|
|
320
|
+
value,
|
|
321
|
+
path=f"annotation[{index}].labels.{key}",
|
|
322
|
+
)
|
|
323
|
+
for key, value in labels.items()
|
|
324
|
+
}
|
|
325
|
+
stage = raw["annotation_stage"]
|
|
326
|
+
if stage == "first_pass":
|
|
327
|
+
if annotator not in assigned:
|
|
328
|
+
raise ValidationError("first-pass annotation came from an unassigned annotator")
|
|
329
|
+
if annotator in first_pass[blind_id]:
|
|
330
|
+
raise ValidationError("duplicate first-pass annotation")
|
|
331
|
+
first_pass[blind_id][annotator] = freeze_mapping(normalized)
|
|
332
|
+
elif stage == "adjudication":
|
|
333
|
+
if annotator in assigned:
|
|
334
|
+
raise ValidationError("adjudication must come from a third annotator")
|
|
335
|
+
if blind_id in adjudications:
|
|
336
|
+
raise ValidationError("duplicate adjudication")
|
|
337
|
+
adjudications[blind_id] = (annotator, freeze_mapping(normalized))
|
|
338
|
+
else:
|
|
339
|
+
raise ValidationError("annotation_stage must be first_pass or adjudication")
|
|
340
|
+
|
|
341
|
+
missing = sorted(
|
|
342
|
+
blind_id
|
|
343
|
+
for blind_id, (_kind, assigned) in assignments.items()
|
|
344
|
+
if set(first_pass.get(blind_id, {})) != set(assigned)
|
|
345
|
+
)
|
|
346
|
+
if missing:
|
|
347
|
+
raise ValidationError(f"measurement annotations omit assigned first passes: {missing[:5]}")
|
|
348
|
+
|
|
349
|
+
field_pairs: dict[tuple[str, str], list[tuple[Any, Any]]] = defaultdict(list)
|
|
350
|
+
disagreement_ids: list[str] = []
|
|
351
|
+
final_labels: dict[str, Mapping[str, Any]] = {}
|
|
352
|
+
for blind_id, (kind, assigned) in assignments.items():
|
|
353
|
+
left = first_pass[blind_id][assigned[0]]
|
|
354
|
+
right = first_pass[blind_id][assigned[1]]
|
|
355
|
+
if set(left) != set(right):
|
|
356
|
+
raise ValidationError("paired first-pass annotations use different label fields")
|
|
357
|
+
disagreed = left != right
|
|
358
|
+
if disagreed:
|
|
359
|
+
disagreement_ids.append(blind_id)
|
|
360
|
+
adjudication = adjudications.get(blind_id)
|
|
361
|
+
if adjudication is not None:
|
|
362
|
+
if set(adjudication[1]) != set(left):
|
|
363
|
+
raise ValidationError("adjudication label fields differ from first pass")
|
|
364
|
+
final_labels[blind_id] = adjudication[1]
|
|
365
|
+
else:
|
|
366
|
+
if blind_id in adjudications:
|
|
367
|
+
raise ValidationError("adjudication is forbidden when first-pass labels agree")
|
|
368
|
+
final_labels[blind_id] = left
|
|
369
|
+
for field_name in sorted(left):
|
|
370
|
+
field_pairs[(kind, field_name)].append((left[field_name], right[field_name]))
|
|
371
|
+
|
|
372
|
+
agreement: list[dict[str, Any]] = []
|
|
373
|
+
for (kind, field_name), pairs in sorted(field_pairs.items()):
|
|
374
|
+
observed = sum(left == right for left, right in pairs) / len(pairs)
|
|
375
|
+
left_counts = Counter(left for left, _right in pairs)
|
|
376
|
+
right_counts = Counter(right for _left, right in pairs)
|
|
377
|
+
categories = set(left_counts) | set(right_counts)
|
|
378
|
+
expected = sum(
|
|
379
|
+
(left_counts[category] / len(pairs)) * (right_counts[category] / len(pairs))
|
|
380
|
+
for category in categories
|
|
381
|
+
)
|
|
382
|
+
kappa = (
|
|
383
|
+
1.0
|
|
384
|
+
if math.isclose(expected, 1.0) and math.isclose(observed, 1.0)
|
|
385
|
+
else (
|
|
386
|
+
(observed - expected) / (1.0 - expected)
|
|
387
|
+
if not math.isclose(expected, 1.0)
|
|
388
|
+
else float("nan")
|
|
389
|
+
)
|
|
390
|
+
)
|
|
391
|
+
if not math.isfinite(kappa):
|
|
392
|
+
raise ValidationError("measurement agreement produced a non-finite kappa")
|
|
393
|
+
pooled_counts = Counter(label for left, right in pairs for label in (left, right))
|
|
394
|
+
pooled_total = 2 * len(pairs)
|
|
395
|
+
expected_disagreement = (
|
|
396
|
+
1.0
|
|
397
|
+
- sum(count * (count - 1) for count in pooled_counts.values())
|
|
398
|
+
/ (pooled_total * (pooled_total - 1))
|
|
399
|
+
if pooled_total > 1
|
|
400
|
+
else 0.0
|
|
401
|
+
)
|
|
402
|
+
observed_disagreement = 1.0 - observed
|
|
403
|
+
alpha = (
|
|
404
|
+
1.0
|
|
405
|
+
if math.isclose(expected_disagreement, 0.0) and math.isclose(observed_disagreement, 0.0)
|
|
406
|
+
else (
|
|
407
|
+
1.0 - observed_disagreement / expected_disagreement
|
|
408
|
+
if not math.isclose(expected_disagreement, 0.0)
|
|
409
|
+
else float("nan")
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
if not math.isfinite(alpha):
|
|
413
|
+
raise ValidationError("measurement agreement produced a non-finite Krippendorff alpha")
|
|
414
|
+
agreement.append(
|
|
415
|
+
{
|
|
416
|
+
"kind": kind,
|
|
417
|
+
"field": field_name,
|
|
418
|
+
"n": len(pairs),
|
|
419
|
+
"percent_agreement": observed,
|
|
420
|
+
"cohen_kappa": kappa,
|
|
421
|
+
"krippendorff_alpha_nominal": alpha,
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
unresolved = sorted(set(disagreement_ids) - set(adjudications))
|
|
426
|
+
result = {
|
|
427
|
+
"schema_version": 1,
|
|
428
|
+
"protocol": MEASUREMENT_ANALYSIS_PROTOCOL,
|
|
429
|
+
"assignment_hash": manifest["assignment_hash"],
|
|
430
|
+
"item_count": len(assignments),
|
|
431
|
+
"first_pass_annotation_count": sum(len(rows) for rows in first_pass.values()),
|
|
432
|
+
"disagreement_count": len(disagreement_ids),
|
|
433
|
+
"adjudicated_count": len(adjudications),
|
|
434
|
+
"unresolved_disagreement_count": len(unresolved),
|
|
435
|
+
"unresolved_blind_ids": unresolved,
|
|
436
|
+
"agreement": agreement,
|
|
437
|
+
"final_label_count": len(final_labels),
|
|
438
|
+
# Preserve the adjudicated item-level evidence. Aggregate counts alone
|
|
439
|
+
# cannot validate an automatic labeler or produce a confusion matrix.
|
|
440
|
+
"final_labels": [
|
|
441
|
+
{
|
|
442
|
+
"blind_id": blind_id,
|
|
443
|
+
"kind": assignments[blind_id][0],
|
|
444
|
+
"labels": to_jsonable(labels),
|
|
445
|
+
}
|
|
446
|
+
for blind_id, labels in sorted(final_labels.items())
|
|
447
|
+
],
|
|
448
|
+
"analysis_complete": not unresolved,
|
|
449
|
+
}
|
|
450
|
+
result["analysis_hash"] = content_hash(result)
|
|
451
|
+
return result
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def compare_automatic_human_audits(
|
|
455
|
+
*,
|
|
456
|
+
automatic_input_labels: Sequence[Mapping[str, Any]],
|
|
457
|
+
input_audit_items: Sequence[Mapping[str, Any]],
|
|
458
|
+
input_final_annotations: Sequence[Mapping[str, Any]],
|
|
459
|
+
automatic_trajectory_labels: Sequence[Mapping[str, Any]],
|
|
460
|
+
trajectory_audit_items: Sequence[Mapping[str, Any]],
|
|
461
|
+
trajectory_final_annotations: Sequence[Mapping[str, Any]],
|
|
462
|
+
) -> dict[str, Any]:
|
|
463
|
+
"""Compare frozen automatic labels with final human adjudications.
|
|
464
|
+
|
|
465
|
+
The function produces the six rows used by the paper's human-audit table,
|
|
466
|
+
plus a full micro confusion matrix for every row. ``uncertain`` human
|
|
467
|
+
labels are retained in the denominator metadata but excluded from binary
|
|
468
|
+
precision/recall rather than silently coerced.
|
|
469
|
+
"""
|
|
470
|
+
|
|
471
|
+
input_auto = {
|
|
472
|
+
f"defect:{row.get('benchmark_id')}:{row.get('checkpoint_id')}": row
|
|
473
|
+
for row in automatic_input_labels
|
|
474
|
+
}
|
|
475
|
+
trajectory_auto = {
|
|
476
|
+
f"trajectory:{row.get('slot_id')}": row for row in automatic_trajectory_labels
|
|
477
|
+
}
|
|
478
|
+
input_items = _audit_item_map(input_audit_items)
|
|
479
|
+
trajectory_items = _audit_item_map(trajectory_audit_items)
|
|
480
|
+
input_human = _annotation_map(input_final_annotations)
|
|
481
|
+
trajectory_human = _annotation_map(trajectory_final_annotations)
|
|
482
|
+
_require_exact_annotation_coverage(input_items, input_human, label="input audit")
|
|
483
|
+
_require_exact_annotation_coverage(
|
|
484
|
+
trajectory_items,
|
|
485
|
+
trajectory_human,
|
|
486
|
+
label="trajectory audit",
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
comparisons: dict[str, list[tuple[bool, bool | None]]] = defaultdict(list)
|
|
490
|
+
defect_fields = (
|
|
491
|
+
"unclear_or_misaligned_request",
|
|
492
|
+
"decomposition_need",
|
|
493
|
+
"missing_external_evidence",
|
|
494
|
+
"clarification_need",
|
|
495
|
+
"context_overload_or_noise",
|
|
496
|
+
)
|
|
497
|
+
eligibility_fields = tuple(
|
|
498
|
+
f"{family}_eligible"
|
|
499
|
+
for family in ("reformulate", "decompose", "acquire", "clarify", "curate")
|
|
500
|
+
)
|
|
501
|
+
for item_id in sorted(input_items):
|
|
502
|
+
try:
|
|
503
|
+
auto = input_auto[item_id]
|
|
504
|
+
except KeyError as exc:
|
|
505
|
+
raise ValidationError(f"input audit item has no automatic label: {item_id}") from exc
|
|
506
|
+
defect_labels = auto.get("defect_labels")
|
|
507
|
+
eligibility_labels = auto.get("eligibility_labels")
|
|
508
|
+
if not isinstance(defect_labels, Mapping) or not isinstance(eligibility_labels, Mapping):
|
|
509
|
+
raise ValidationError("automatic input label fields are invalid")
|
|
510
|
+
labels = input_human[item_id]
|
|
511
|
+
for field_name in defect_fields:
|
|
512
|
+
comparisons["Defect labeling"].append(
|
|
513
|
+
(bool(defect_labels.get(field_name)), _human_binary(labels.get(field_name)))
|
|
514
|
+
)
|
|
515
|
+
for field_name in eligibility_fields:
|
|
516
|
+
comparisons["Eligibility"].append(
|
|
517
|
+
(bool(eligibility_labels.get(field_name)), _human_binary(labels.get(field_name)))
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
trajectory_mapping = {
|
|
521
|
+
"Contract preservation": "overall_contract_valid",
|
|
522
|
+
"Entity preservation": "entity_preserved",
|
|
523
|
+
"Numeric preservation": "numeric_preserved",
|
|
524
|
+
"Constraint preservation": "constraint_preserved",
|
|
525
|
+
}
|
|
526
|
+
for item_id in sorted(trajectory_items):
|
|
527
|
+
try:
|
|
528
|
+
auto = trajectory_auto[item_id]
|
|
529
|
+
except KeyError as exc:
|
|
530
|
+
raise ValidationError(
|
|
531
|
+
f"trajectory audit item has no automatic label: {item_id}"
|
|
532
|
+
) from exc
|
|
533
|
+
checkpoints = auto.get("checkpoint_results")
|
|
534
|
+
if not isinstance(checkpoints, Sequence) or isinstance(checkpoints, (str, bytes)):
|
|
535
|
+
raise ValidationError("automatic trajectory checkpoint results are invalid")
|
|
536
|
+
checks = []
|
|
537
|
+
for row in checkpoints:
|
|
538
|
+
if not isinstance(row, Mapping) or not isinstance(row.get("checks"), Mapping):
|
|
539
|
+
raise ValidationError("automatic trajectory checkpoint result is invalid")
|
|
540
|
+
checks.append(row["checks"])
|
|
541
|
+
auto_values = {
|
|
542
|
+
"Contract preservation": bool(auto.get("auto_contract_valid")),
|
|
543
|
+
"Entity preservation": all(bool(row.get("entity_preservation")) for row in checks),
|
|
544
|
+
"Numeric preservation": all(bool(row.get("numeric_preservation")) for row in checks),
|
|
545
|
+
"Constraint preservation": all(
|
|
546
|
+
bool(row.get("constraint_preservation")) for row in checks
|
|
547
|
+
),
|
|
548
|
+
}
|
|
549
|
+
human = trajectory_human[item_id]
|
|
550
|
+
for measurement, field_name in trajectory_mapping.items():
|
|
551
|
+
comparisons[measurement].append(
|
|
552
|
+
(auto_values[measurement], _human_binary(human.get(field_name)))
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
ordered_names = (
|
|
556
|
+
"Defect labeling",
|
|
557
|
+
"Eligibility",
|
|
558
|
+
"Contract preservation",
|
|
559
|
+
"Entity preservation",
|
|
560
|
+
"Numeric preservation",
|
|
561
|
+
"Constraint preservation",
|
|
562
|
+
)
|
|
563
|
+
rows = [_confusion_row(name, comparisons[name]) for name in ordered_names]
|
|
564
|
+
result = {
|
|
565
|
+
"schema_version": 1,
|
|
566
|
+
"protocol": AUTOMATIC_HUMAN_VALIDATION_PROTOCOL,
|
|
567
|
+
"positive_class": (
|
|
568
|
+
"defect present for Defect labeling; eligible/preserved for all other rows"
|
|
569
|
+
),
|
|
570
|
+
"uncertain_policy": "excluded_from_binary_metrics_and_reported",
|
|
571
|
+
"input_audit_item_count": len(input_items),
|
|
572
|
+
"trajectory_audit_item_count": len(trajectory_items),
|
|
573
|
+
"rows": rows,
|
|
574
|
+
}
|
|
575
|
+
result["validation_hash"] = content_hash(result)
|
|
576
|
+
return result
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def _audit_item_map(rows: Sequence[Mapping[str, Any]]) -> dict[str, Mapping[str, Any]]:
|
|
580
|
+
values: dict[str, Mapping[str, Any]] = {}
|
|
581
|
+
for index, row in enumerate(rows):
|
|
582
|
+
if not isinstance(row, Mapping):
|
|
583
|
+
raise ValidationError(f"audit item {index} must be an object")
|
|
584
|
+
raw_id = row.get("audit_item_id", row.get("item_id"))
|
|
585
|
+
item_id = _text(raw_id, label=f"audit item {index} ID")
|
|
586
|
+
if item_id in values:
|
|
587
|
+
raise ValidationError("audit item IDs must be unique")
|
|
588
|
+
values[item_id] = row
|
|
589
|
+
if not values:
|
|
590
|
+
raise ValidationError("audit items must be non-empty")
|
|
591
|
+
return values
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def _annotation_map(rows: Sequence[Mapping[str, Any]]) -> dict[str, Mapping[str, Any]]:
|
|
595
|
+
values: dict[str, Mapping[str, Any]] = {}
|
|
596
|
+
for index, row in enumerate(rows):
|
|
597
|
+
if not isinstance(row, Mapping):
|
|
598
|
+
raise ValidationError(f"final annotation {index} must be an object")
|
|
599
|
+
item_id = _text(row.get("item_id"), label=f"final annotation {index} item_id")
|
|
600
|
+
labels = row.get("labels")
|
|
601
|
+
if not isinstance(labels, Mapping) or not labels:
|
|
602
|
+
raise ValidationError("final annotation labels must be a non-empty object")
|
|
603
|
+
if item_id in values:
|
|
604
|
+
raise ValidationError("final annotations contain duplicate item IDs")
|
|
605
|
+
values[item_id] = labels
|
|
606
|
+
return values
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def _require_exact_annotation_coverage(
|
|
610
|
+
items: Mapping[str, Any], annotations: Mapping[str, Any], *, label: str
|
|
611
|
+
) -> None:
|
|
612
|
+
if set(items) != set(annotations):
|
|
613
|
+
missing = sorted(set(items) - set(annotations))
|
|
614
|
+
extra = sorted(set(annotations) - set(items))
|
|
615
|
+
raise ValidationError(
|
|
616
|
+
f"{label} final annotation coverage differs: missing={missing[:5]}, extra={extra[:5]}"
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def _human_binary(value: Any) -> bool | None:
|
|
621
|
+
if isinstance(value, bool):
|
|
622
|
+
return value
|
|
623
|
+
if isinstance(value, str):
|
|
624
|
+
normalized = value.strip().casefold()
|
|
625
|
+
if normalized == "yes":
|
|
626
|
+
return True
|
|
627
|
+
if normalized == "no":
|
|
628
|
+
return False
|
|
629
|
+
if normalized == "uncertain":
|
|
630
|
+
return None
|
|
631
|
+
raise ValidationError("human audit binary label must be yes, no, uncertain, or boolean")
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def _confusion_row(name: str, pairs: Sequence[tuple[bool, bool | None]]) -> dict[str, Any]:
|
|
635
|
+
certain = [(automatic, human) for automatic, human in pairs if human is not None]
|
|
636
|
+
tp = sum(automatic and bool(human) for automatic, human in certain)
|
|
637
|
+
fp = sum(automatic and not bool(human) for automatic, human in certain)
|
|
638
|
+
tn = sum(not automatic and not bool(human) for automatic, human in certain)
|
|
639
|
+
fn = sum(not automatic and bool(human) for automatic, human in certain)
|
|
640
|
+
n = len(certain)
|
|
641
|
+
agreement = (tp + tn) / n if n else None
|
|
642
|
+
precision = tp / (tp + fp) if tp + fp else None
|
|
643
|
+
recall = tp / (tp + fn) if tp + fn else None
|
|
644
|
+
f1 = (
|
|
645
|
+
2 * precision * recall / (precision + recall)
|
|
646
|
+
if precision is not None and recall is not None and precision + recall
|
|
647
|
+
else None
|
|
648
|
+
)
|
|
649
|
+
return {
|
|
650
|
+
"measurement": name,
|
|
651
|
+
"n_total": len(pairs),
|
|
652
|
+
"n_evaluable": n,
|
|
653
|
+
"n_uncertain": len(pairs) - n,
|
|
654
|
+
"tp": tp,
|
|
655
|
+
"fp": fp,
|
|
656
|
+
"tn": tn,
|
|
657
|
+
"fn": fn,
|
|
658
|
+
"agreement": agreement,
|
|
659
|
+
"precision": precision,
|
|
660
|
+
"recall": recall,
|
|
661
|
+
"f1": f1,
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
__all__ = [
|
|
666
|
+
"AUTOMATIC_HUMAN_VALIDATION_PROTOCOL",
|
|
667
|
+
"DEFAULT_MEASUREMENT_COUNTS",
|
|
668
|
+
"MEASUREMENT_ANALYSIS_PROTOCOL",
|
|
669
|
+
"MEASUREMENT_ASSIGNMENT_PROTOCOL",
|
|
670
|
+
"MEASUREMENT_KINDS",
|
|
671
|
+
"MeasurementItem",
|
|
672
|
+
"analyze_measurement_annotations",
|
|
673
|
+
"build_measurement_assignment",
|
|
674
|
+
"compare_automatic_human_audits",
|
|
675
|
+
]
|