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,444 @@
|
|
|
1
|
+
"""Create-only, versioned storage for non-blocking measurement audits."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import tempfile
|
|
9
|
+
from collections.abc import Mapping, Sequence
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from agentinputlab.contracts.serialization import canonical_json, content_hash
|
|
15
|
+
|
|
16
|
+
ANNOTATION_VERSION_PROTOCOL = "agentinputlab.annotation-version.v1"
|
|
17
|
+
ANNOTATION_SELECTION_PROTOCOL = "agentinputlab.annotation-selection.v1"
|
|
18
|
+
ANNOTATION_KINDS = frozenset({"input_audit", "trajectory_audit"})
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AnnotationStoreError(ValueError):
|
|
22
|
+
"""Raised when an annotation version would be ambiguous or mutable."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def validate_annotation_selection(
|
|
26
|
+
root: str | Path,
|
|
27
|
+
*,
|
|
28
|
+
kind: str,
|
|
29
|
+
) -> Mapping[str, Any] | None:
|
|
30
|
+
"""Return the selected completed audit version, or fail on ambiguity.
|
|
31
|
+
|
|
32
|
+
Automatic measurements do not require a human audit to exist. Once at
|
|
33
|
+
least one completed audit does exist, however, experiment and plotting
|
|
34
|
+
entry points must bind an explicit selection receipt instead of silently
|
|
35
|
+
choosing the newest file on disk.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
if kind not in ANNOTATION_KINDS:
|
|
39
|
+
raise AnnotationStoreError("annotation kind must be input_audit or trajectory_audit")
|
|
40
|
+
root_path = Path(root).expanduser().resolve()
|
|
41
|
+
if root_path.is_symlink():
|
|
42
|
+
raise AnnotationStoreError("annotation root must not be a symbolic link")
|
|
43
|
+
if not root_path.exists():
|
|
44
|
+
return None
|
|
45
|
+
if not root_path.is_dir():
|
|
46
|
+
raise AnnotationStoreError("annotation root must be a directory")
|
|
47
|
+
|
|
48
|
+
completed: dict[str, Mapping[str, Any]] = {}
|
|
49
|
+
for path in sorted(root_path.glob(f"{kind}-v[0-9][0-9][0-9]/manifest.json")):
|
|
50
|
+
value = _read_json(path)
|
|
51
|
+
items_hash = value.get("items_sha256")
|
|
52
|
+
if not isinstance(items_hash, str) or not items_hash:
|
|
53
|
+
raise AnnotationStoreError(f"annotation manifest has no items hash: {path}")
|
|
54
|
+
_validate_manifest(value, kind=kind, items_hash=items_hash)
|
|
55
|
+
if value["status"] == "complete":
|
|
56
|
+
annotations_path = path.parent / "annotations.jsonl"
|
|
57
|
+
if annotations_path.is_symlink() or not annotations_path.is_file():
|
|
58
|
+
raise AnnotationStoreError(
|
|
59
|
+
f"completed annotation data is unavailable: {annotations_path}"
|
|
60
|
+
)
|
|
61
|
+
if _sha256(annotations_path) != value.get("annotations_sha256"):
|
|
62
|
+
raise AnnotationStoreError(
|
|
63
|
+
f"completed annotation data hash does not match its manifest: {annotations_path}"
|
|
64
|
+
)
|
|
65
|
+
completed[str(value["version_id"])] = value
|
|
66
|
+
if not completed:
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
selection_path = root_path / f"{kind}-selection.json"
|
|
70
|
+
if selection_path.is_symlink() or not selection_path.is_file():
|
|
71
|
+
versions = ", ".join(sorted(completed))
|
|
72
|
+
raise AnnotationStoreError(
|
|
73
|
+
f"completed {kind} annotation exists ({versions}) but no version is selected; "
|
|
74
|
+
"open agentinputlab-annotate or run select_annotation_version.py"
|
|
75
|
+
)
|
|
76
|
+
selection = _read_json(selection_path)
|
|
77
|
+
expected = selection.get("selection_hash")
|
|
78
|
+
unhashed = {key: item for key, item in selection.items() if key != "selection_hash"}
|
|
79
|
+
version_id = selection.get("version_id")
|
|
80
|
+
if (
|
|
81
|
+
selection.get("schema_version") != 1
|
|
82
|
+
or selection.get("protocol") != ANNOTATION_SELECTION_PROTOCOL
|
|
83
|
+
or selection.get("kind") != kind
|
|
84
|
+
or expected != content_hash(unhashed)
|
|
85
|
+
or version_id not in completed
|
|
86
|
+
):
|
|
87
|
+
raise AnnotationStoreError(f"selected {kind} annotation receipt is invalid")
|
|
88
|
+
manifest = completed[str(version_id)]
|
|
89
|
+
if (
|
|
90
|
+
selection.get("version_manifest_hash") != manifest["manifest_hash"]
|
|
91
|
+
or selection.get("items_sha256") != manifest["items_sha256"]
|
|
92
|
+
or selection.get("annotations_sha256") != manifest["annotations_sha256"]
|
|
93
|
+
):
|
|
94
|
+
raise AnnotationStoreError(f"selected {kind} annotation hashes do not match its version")
|
|
95
|
+
return dict(selection)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class AnnotationStore:
|
|
99
|
+
"""Persist one local audit census in immutable, explicitly selected versions."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, *, items_path: str | Path, root: str | Path, kind: str) -> None:
|
|
102
|
+
if kind not in ANNOTATION_KINDS:
|
|
103
|
+
raise AnnotationStoreError("annotation kind must be input_audit or trajectory_audit")
|
|
104
|
+
self.items_path = Path(items_path).expanduser().resolve()
|
|
105
|
+
self.root = Path(root).expanduser().resolve()
|
|
106
|
+
self.kind = kind
|
|
107
|
+
if self.items_path.is_symlink() or not self.items_path.is_file():
|
|
108
|
+
raise AnnotationStoreError(f"annotation item file is unavailable: {self.items_path}")
|
|
109
|
+
self.items = _load_items(self.items_path)
|
|
110
|
+
self.items_hash = _sha256(self.items_path)
|
|
111
|
+
self.root.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
|
|
113
|
+
def status(self) -> dict[str, Any]:
|
|
114
|
+
versions = self.list_versions()
|
|
115
|
+
completed = [row for row in versions if row["status"] == "complete"]
|
|
116
|
+
selection = self._selection()
|
|
117
|
+
return {
|
|
118
|
+
"schema_version": 1,
|
|
119
|
+
"service": "agentinputlab-annotation",
|
|
120
|
+
"kind": self.kind,
|
|
121
|
+
"item_count": len(self.items),
|
|
122
|
+
"items_sha256": self.items_hash,
|
|
123
|
+
"versions": versions,
|
|
124
|
+
"completed_versions": len(completed),
|
|
125
|
+
"completed_version_exists": bool(completed),
|
|
126
|
+
"requires_reuse_or_new_version_choice": bool(completed),
|
|
127
|
+
"selected_version": selection["version_id"] if selection is not None else None,
|
|
128
|
+
"selection_path": str(self.selection_path),
|
|
129
|
+
"execution_depends_on_completion": False,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def selection_path(self) -> Path:
|
|
134
|
+
return self.root / f"{self.kind}-selection.json"
|
|
135
|
+
|
|
136
|
+
def list_versions(self) -> list[dict[str, Any]]:
|
|
137
|
+
rows: list[dict[str, Any]] = []
|
|
138
|
+
for path in sorted(self.root.glob(f"{self.kind}-v[0-9][0-9][0-9]/manifest.json")):
|
|
139
|
+
try:
|
|
140
|
+
manifest = _read_json(path)
|
|
141
|
+
_validate_manifest(manifest, kind=self.kind, items_hash=self.items_hash)
|
|
142
|
+
except (OSError, ValueError, json.JSONDecodeError):
|
|
143
|
+
continue
|
|
144
|
+
rows.append(_public_manifest(manifest))
|
|
145
|
+
return rows
|
|
146
|
+
|
|
147
|
+
def create_version(self, *, annotator_id: str) -> dict[str, Any]:
|
|
148
|
+
annotator_id = _text(annotator_id, label="annotator_id")
|
|
149
|
+
existing = self.list_versions()
|
|
150
|
+
number = max((int(row["version_id"].rsplit("v", 1)[1]) for row in existing), default=0) + 1
|
|
151
|
+
version_id = f"{self.kind}-v{number:03d}"
|
|
152
|
+
directory = self.root / version_id
|
|
153
|
+
directory.mkdir(parents=False, exist_ok=False)
|
|
154
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
155
|
+
without_hash = {
|
|
156
|
+
"schema_version": 1,
|
|
157
|
+
"protocol": ANNOTATION_VERSION_PROTOCOL,
|
|
158
|
+
"version_id": version_id,
|
|
159
|
+
"kind": self.kind,
|
|
160
|
+
"annotator_id": annotator_id,
|
|
161
|
+
"items_path": str(self.items_path),
|
|
162
|
+
"items_sha256": self.items_hash,
|
|
163
|
+
"item_count": len(self.items),
|
|
164
|
+
"annotation_count": 0,
|
|
165
|
+
"status": "in_progress",
|
|
166
|
+
"created_at": now,
|
|
167
|
+
"completed_at": None,
|
|
168
|
+
"annotations_sha256": None,
|
|
169
|
+
"supersedes_completed_version": any(row["status"] == "complete" for row in existing),
|
|
170
|
+
}
|
|
171
|
+
manifest = {**without_hash, "manifest_hash": content_hash(without_hash)}
|
|
172
|
+
_write_atomic(directory / "manifest.json", manifest)
|
|
173
|
+
_write_atomic(directory / "annotations.jsonl", b"")
|
|
174
|
+
return _public_manifest(manifest)
|
|
175
|
+
|
|
176
|
+
def payload(self, version_id: str) -> dict[str, Any]:
|
|
177
|
+
manifest = self._manifest(version_id)
|
|
178
|
+
annotations = self._annotations(version_id)
|
|
179
|
+
return {
|
|
180
|
+
"manifest": _public_manifest(manifest),
|
|
181
|
+
"items": self.items,
|
|
182
|
+
"annotations": annotations,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
def save(self, version_id: str, annotation: Mapping[str, Any]) -> dict[str, Any]:
|
|
186
|
+
manifest = self._manifest(version_id)
|
|
187
|
+
if manifest["status"] == "complete":
|
|
188
|
+
raise AnnotationStoreError(
|
|
189
|
+
"completed annotations are immutable; create a new version to re-annotate"
|
|
190
|
+
)
|
|
191
|
+
item_id = _text(annotation.get("item_id"), label="annotation item_id")
|
|
192
|
+
if item_id not in {row["item_id"] for row in self.items}:
|
|
193
|
+
raise AnnotationStoreError("annotation item_id is outside the frozen audit census")
|
|
194
|
+
labels = annotation.get("labels")
|
|
195
|
+
if not isinstance(labels, Mapping) or not labels:
|
|
196
|
+
raise AnnotationStoreError("annotation labels must be a non-empty object")
|
|
197
|
+
if any(
|
|
198
|
+
not isinstance(key, str)
|
|
199
|
+
or not key
|
|
200
|
+
or not isinstance(value, (str, bool, int))
|
|
201
|
+
or (isinstance(value, str) and not value.strip())
|
|
202
|
+
for key, value in labels.items()
|
|
203
|
+
):
|
|
204
|
+
raise AnnotationStoreError("annotation labels contain invalid values")
|
|
205
|
+
notes = annotation.get("notes", "")
|
|
206
|
+
if not isinstance(notes, str) or len(notes) > 4000:
|
|
207
|
+
raise AnnotationStoreError("annotation notes must be a string of at most 4000 chars")
|
|
208
|
+
rows = {row["item_id"]: row for row in self._annotations(version_id)}
|
|
209
|
+
row_without_hash = {
|
|
210
|
+
"schema_version": 1,
|
|
211
|
+
"version_id": version_id,
|
|
212
|
+
"kind": self.kind,
|
|
213
|
+
"item_id": item_id,
|
|
214
|
+
"annotator_id": manifest["annotator_id"],
|
|
215
|
+
"labels": dict(sorted(labels.items())),
|
|
216
|
+
"notes": notes,
|
|
217
|
+
"saved_at": datetime.now(timezone.utc).isoformat(),
|
|
218
|
+
}
|
|
219
|
+
rows[item_id] = {**row_without_hash, "annotation_hash": content_hash(row_without_hash)}
|
|
220
|
+
ordered = [rows[key] for key in sorted(rows)]
|
|
221
|
+
annotations_path = self.root / version_id / "annotations.jsonl"
|
|
222
|
+
_write_atomic(annotations_path, _jsonl_bytes(ordered))
|
|
223
|
+
updated = self._updated_manifest(
|
|
224
|
+
manifest,
|
|
225
|
+
annotation_count=len(ordered),
|
|
226
|
+
annotations_sha256=_sha256(annotations_path),
|
|
227
|
+
)
|
|
228
|
+
_write_atomic(self.root / version_id / "manifest.json", updated)
|
|
229
|
+
return {
|
|
230
|
+
"saved": True,
|
|
231
|
+
"version_id": version_id,
|
|
232
|
+
"item_id": item_id,
|
|
233
|
+
"annotation_count": len(ordered),
|
|
234
|
+
"item_count": len(self.items),
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
def complete(self, version_id: str) -> dict[str, Any]:
|
|
238
|
+
manifest = self._manifest(version_id)
|
|
239
|
+
if manifest["status"] == "complete":
|
|
240
|
+
return _public_manifest(manifest)
|
|
241
|
+
annotations = self._annotations(version_id)
|
|
242
|
+
observed = {row["item_id"] for row in annotations}
|
|
243
|
+
expected = {row["item_id"] for row in self.items}
|
|
244
|
+
if observed != expected:
|
|
245
|
+
raise AnnotationStoreError(
|
|
246
|
+
f"cannot complete annotation: {len(expected - observed)} audit items remain"
|
|
247
|
+
)
|
|
248
|
+
annotations_path = self.root / version_id / "annotations.jsonl"
|
|
249
|
+
updated = self._updated_manifest(
|
|
250
|
+
manifest,
|
|
251
|
+
annotation_count=len(annotations),
|
|
252
|
+
annotations_sha256=_sha256(annotations_path),
|
|
253
|
+
status="complete",
|
|
254
|
+
completed_at=datetime.now(timezone.utc).isoformat(),
|
|
255
|
+
)
|
|
256
|
+
_write_atomic(self.root / version_id / "manifest.json", updated)
|
|
257
|
+
return _public_manifest(updated)
|
|
258
|
+
|
|
259
|
+
def select(self, version_id: str, *, output: str | Path | None = None) -> dict[str, Any]:
|
|
260
|
+
manifest = self._manifest(version_id)
|
|
261
|
+
if manifest["status"] != "complete":
|
|
262
|
+
raise AnnotationStoreError("only a complete annotation version may be selected")
|
|
263
|
+
output_path = (
|
|
264
|
+
Path(output).expanduser().resolve() if output is not None else self.selection_path
|
|
265
|
+
)
|
|
266
|
+
without_hash = {
|
|
267
|
+
"schema_version": 1,
|
|
268
|
+
"protocol": ANNOTATION_SELECTION_PROTOCOL,
|
|
269
|
+
"kind": self.kind,
|
|
270
|
+
"version_id": version_id,
|
|
271
|
+
"version_manifest_hash": manifest["manifest_hash"],
|
|
272
|
+
"items_sha256": self.items_hash,
|
|
273
|
+
"annotations_sha256": manifest["annotations_sha256"],
|
|
274
|
+
"selected_at": datetime.now(timezone.utc).isoformat(),
|
|
275
|
+
}
|
|
276
|
+
selection = {**without_hash, "selection_hash": content_hash(without_hash)}
|
|
277
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
278
|
+
_write_atomic(
|
|
279
|
+
output_path, (json.dumps(selection, indent=2, sort_keys=True) + "\n").encode()
|
|
280
|
+
)
|
|
281
|
+
return selection
|
|
282
|
+
|
|
283
|
+
def _selection(self) -> Mapping[str, Any] | None:
|
|
284
|
+
path = self.selection_path
|
|
285
|
+
if path.is_symlink() or not path.is_file():
|
|
286
|
+
return None
|
|
287
|
+
value = _read_json(path)
|
|
288
|
+
expected = value.get("selection_hash")
|
|
289
|
+
unhashed = {key: item for key, item in value.items() if key != "selection_hash"}
|
|
290
|
+
if (
|
|
291
|
+
value.get("schema_version") != 1
|
|
292
|
+
or value.get("protocol") != ANNOTATION_SELECTION_PROTOCOL
|
|
293
|
+
or value.get("kind") != self.kind
|
|
294
|
+
or value.get("items_sha256") != self.items_hash
|
|
295
|
+
or expected != content_hash(unhashed)
|
|
296
|
+
):
|
|
297
|
+
raise AnnotationStoreError("annotation selection receipt is invalid")
|
|
298
|
+
manifest = self._manifest(str(value.get("version_id", "")))
|
|
299
|
+
annotations_path = self.root / manifest["version_id"] / "annotations.jsonl"
|
|
300
|
+
if (
|
|
301
|
+
manifest["status"] != "complete"
|
|
302
|
+
or value.get("version_manifest_hash") != manifest["manifest_hash"]
|
|
303
|
+
or value.get("annotations_sha256") != manifest["annotations_sha256"]
|
|
304
|
+
or annotations_path.is_symlink()
|
|
305
|
+
or not annotations_path.is_file()
|
|
306
|
+
or _sha256(annotations_path) != manifest["annotations_sha256"]
|
|
307
|
+
):
|
|
308
|
+
raise AnnotationStoreError("selected annotation version is unavailable or modified")
|
|
309
|
+
return value
|
|
310
|
+
|
|
311
|
+
def _manifest(self, version_id: str) -> Mapping[str, Any]:
|
|
312
|
+
version_id = _text(version_id, label="version_id")
|
|
313
|
+
if not version_id.startswith(f"{self.kind}-v") or not version_id[-3:].isdigit():
|
|
314
|
+
raise AnnotationStoreError("annotation version_id is invalid")
|
|
315
|
+
path = self.root / version_id / "manifest.json"
|
|
316
|
+
if path.is_symlink() or not path.is_file():
|
|
317
|
+
raise AnnotationStoreError("annotation version does not exist")
|
|
318
|
+
manifest = _read_json(path)
|
|
319
|
+
_validate_manifest(manifest, kind=self.kind, items_hash=self.items_hash)
|
|
320
|
+
return manifest
|
|
321
|
+
|
|
322
|
+
def _annotations(self, version_id: str) -> list[dict[str, Any]]:
|
|
323
|
+
path = self.root / version_id / "annotations.jsonl"
|
|
324
|
+
if path.is_symlink() or not path.is_file():
|
|
325
|
+
raise AnnotationStoreError("annotation version data is unavailable")
|
|
326
|
+
rows = []
|
|
327
|
+
for line_number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
|
328
|
+
if not line.strip():
|
|
329
|
+
continue
|
|
330
|
+
value = json.loads(line)
|
|
331
|
+
if not isinstance(value, Mapping) or value.get("version_id") != version_id:
|
|
332
|
+
raise AnnotationStoreError(f"invalid annotation row at line {line_number}")
|
|
333
|
+
expected = value.get("annotation_hash")
|
|
334
|
+
unhashed = {key: item for key, item in value.items() if key != "annotation_hash"}
|
|
335
|
+
if expected != content_hash(unhashed):
|
|
336
|
+
raise AnnotationStoreError(f"annotation hash mismatch at line {line_number}")
|
|
337
|
+
rows.append(dict(value))
|
|
338
|
+
if len({row["item_id"] for row in rows}) != len(rows):
|
|
339
|
+
raise AnnotationStoreError("annotation version contains duplicate items")
|
|
340
|
+
return rows
|
|
341
|
+
|
|
342
|
+
@staticmethod
|
|
343
|
+
def _updated_manifest(manifest: Mapping[str, Any], **changes: Any) -> dict[str, Any]:
|
|
344
|
+
value = {key: item for key, item in manifest.items() if key != "manifest_hash"}
|
|
345
|
+
value.update(changes)
|
|
346
|
+
return {**value, "manifest_hash": content_hash(value)}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _load_items(path: Path) -> list[dict[str, Any]]:
|
|
350
|
+
rows = []
|
|
351
|
+
for line_number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
|
352
|
+
if not line.strip():
|
|
353
|
+
continue
|
|
354
|
+
value = json.loads(line)
|
|
355
|
+
if not isinstance(value, Mapping):
|
|
356
|
+
raise AnnotationStoreError(f"annotation item {line_number} must be an object")
|
|
357
|
+
item_id = value.get("audit_item_id", value.get("item_id"))
|
|
358
|
+
item_id = _text(item_id, label=f"annotation item {line_number} ID")
|
|
359
|
+
rows.append({"item_id": item_id, **dict(value)})
|
|
360
|
+
if not rows or len({row["item_id"] for row in rows}) != len(rows):
|
|
361
|
+
raise AnnotationStoreError("annotation items must be non-empty and unique")
|
|
362
|
+
return rows
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _validate_manifest(value: Mapping[str, Any], *, kind: str, items_hash: str) -> None:
|
|
366
|
+
expected = value.get("manifest_hash")
|
|
367
|
+
unhashed = {key: item for key, item in value.items() if key != "manifest_hash"}
|
|
368
|
+
if (
|
|
369
|
+
value.get("schema_version") != 1
|
|
370
|
+
or value.get("protocol") != ANNOTATION_VERSION_PROTOCOL
|
|
371
|
+
or value.get("kind") != kind
|
|
372
|
+
or value.get("items_sha256") != items_hash
|
|
373
|
+
or value.get("status") not in {"in_progress", "complete"}
|
|
374
|
+
or expected != content_hash(unhashed)
|
|
375
|
+
):
|
|
376
|
+
raise AnnotationStoreError("annotation version manifest is invalid")
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _public_manifest(value: Mapping[str, Any]) -> dict[str, Any]:
|
|
380
|
+
return {
|
|
381
|
+
key: value[key]
|
|
382
|
+
for key in (
|
|
383
|
+
"version_id",
|
|
384
|
+
"kind",
|
|
385
|
+
"annotator_id",
|
|
386
|
+
"item_count",
|
|
387
|
+
"annotation_count",
|
|
388
|
+
"status",
|
|
389
|
+
"created_at",
|
|
390
|
+
"completed_at",
|
|
391
|
+
"supersedes_completed_version",
|
|
392
|
+
"manifest_hash",
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _write_atomic(path: Path, data: bytes | Mapping[str, Any]) -> None:
|
|
398
|
+
if isinstance(data, Mapping):
|
|
399
|
+
data = (json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True) + "\n").encode()
|
|
400
|
+
descriptor, temporary = tempfile.mkstemp(prefix=".partial-", dir=path.parent)
|
|
401
|
+
try:
|
|
402
|
+
with os.fdopen(descriptor, "wb") as handle:
|
|
403
|
+
handle.write(data)
|
|
404
|
+
handle.flush()
|
|
405
|
+
os.fsync(handle.fileno())
|
|
406
|
+
os.replace(temporary, path)
|
|
407
|
+
finally:
|
|
408
|
+
if os.path.exists(temporary):
|
|
409
|
+
os.unlink(temporary)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _jsonl_bytes(rows: Sequence[Mapping[str, Any]]) -> bytes:
|
|
413
|
+
return "".join(canonical_json(row) + "\n" for row in rows).encode()
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _sha256(path: Path) -> str:
|
|
417
|
+
digest = hashlib.sha256()
|
|
418
|
+
with path.open("rb") as handle:
|
|
419
|
+
for block in iter(lambda: handle.read(1024 * 1024), b""):
|
|
420
|
+
digest.update(block)
|
|
421
|
+
return digest.hexdigest()
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _read_json(path: Path) -> Mapping[str, Any]:
|
|
425
|
+
value = json.loads(path.read_text(encoding="utf-8"))
|
|
426
|
+
if not isinstance(value, Mapping):
|
|
427
|
+
raise AnnotationStoreError("annotation manifest must be an object")
|
|
428
|
+
return value
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def _text(value: Any, *, label: str) -> str:
|
|
432
|
+
if not isinstance(value, str) or not value.strip():
|
|
433
|
+
raise AnnotationStoreError(f"{label} must be a non-empty string")
|
|
434
|
+
return value.strip()
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
__all__ = [
|
|
438
|
+
"ANNOTATION_KINDS",
|
|
439
|
+
"ANNOTATION_SELECTION_PROTOCOL",
|
|
440
|
+
"ANNOTATION_VERSION_PROTOCOL",
|
|
441
|
+
"AnnotationStore",
|
|
442
|
+
"AnnotationStoreError",
|
|
443
|
+
"validate_annotation_selection",
|
|
444
|
+
]
|