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
agentinputlab/api.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""Small Python API and authenticated loopback HTTP transport for the SDK.
|
|
2
|
+
|
|
3
|
+
No experiment archives, remote imports, arbitrary commands or client-supplied
|
|
4
|
+
backend URLs are accepted. Operators configure dependencies in Python.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import hmac
|
|
11
|
+
import inspect
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import threading
|
|
15
|
+
from collections.abc import Callable, Mapping
|
|
16
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from agentinputlab import ContextBlock, ExecutionBudget, FrozenAgentState, Message
|
|
20
|
+
from agentinputlab.benchmarks import BenchmarkRegistry
|
|
21
|
+
from agentinputlab.contracts import InterventionContext
|
|
22
|
+
from agentinputlab.contracts.errors import (
|
|
23
|
+
AgentInputLabError,
|
|
24
|
+
AlgorithmOutputError,
|
|
25
|
+
AlgorithmUnavailableError,
|
|
26
|
+
BudgetExceededError,
|
|
27
|
+
CapabilityError,
|
|
28
|
+
RegistryError,
|
|
29
|
+
ValidationError,
|
|
30
|
+
)
|
|
31
|
+
from agentinputlab.contracts.serialization import to_jsonable
|
|
32
|
+
from agentinputlab.registry import AlgorithmRegistry
|
|
33
|
+
from agentinputlab.runner import CompositeInterventionRunner, InterventionRunner
|
|
34
|
+
from agentinputlab.selectors import NoOpIntervention
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def create_intervention(release_id: str, **parameters: Any) -> Any:
|
|
38
|
+
"""Instantiate a registered profile; optional runtime assets remain explicit.
|
|
39
|
+
|
|
40
|
+
Parameters use the adapter's original Python signature. This does not turn
|
|
41
|
+
composite agents into input-patch methods or assert benchmark eligibility.
|
|
42
|
+
"""
|
|
43
|
+
if release_id == "baseline.no_op":
|
|
44
|
+
return NoOpIntervention(**parameters)
|
|
45
|
+
factory = AlgorithmRegistry.default().resolve_entrypoint(release_id)
|
|
46
|
+
signature = inspect.signature(factory)
|
|
47
|
+
if "release_id" in signature.parameters:
|
|
48
|
+
parameters["release_id"] = release_id
|
|
49
|
+
signature.bind(**parameters)
|
|
50
|
+
result = factory(**parameters)
|
|
51
|
+
actual = getattr(result, "release_id", release_id)
|
|
52
|
+
if actual != release_id:
|
|
53
|
+
raise ValidationError(
|
|
54
|
+
f"registered profile {release_id} constructed a different release: {actual}"
|
|
55
|
+
)
|
|
56
|
+
return result
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_intervention(
|
|
60
|
+
intervention: Any, state: FrozenAgentState, context: InterventionContext | None = None
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
"""Run one patch intervention, preserving the native trace, budget and errors.
|
|
63
|
+
|
|
64
|
+
A successful patch is NOT a benchmark score. Continue the patched state in
|
|
65
|
+
the benchmark's native runtime and official scorer to obtain an endpoint.
|
|
66
|
+
"""
|
|
67
|
+
record = InterventionRunner().run(intervention, state, context or InterventionContext())
|
|
68
|
+
return {
|
|
69
|
+
"record": to_jsonable(record),
|
|
70
|
+
"state": to_jsonable(record.patch.apply(state)),
|
|
71
|
+
"scored": False,
|
|
72
|
+
"benchmark_score": None,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def run_composite_intervention(
|
|
77
|
+
intervention: Any, state: FrozenAgentState, context: InterventionContext | None = None
|
|
78
|
+
) -> dict[str, Any]:
|
|
79
|
+
"""Run an answer-producing architecture with observed calls and cost.
|
|
80
|
+
|
|
81
|
+
The result is not a state patch or a benchmark score. The independent
|
|
82
|
+
benchmark provider/scorer still evaluates the returned final answer.
|
|
83
|
+
"""
|
|
84
|
+
record = CompositeInterventionRunner().run(
|
|
85
|
+
intervention, state, context or InterventionContext()
|
|
86
|
+
)
|
|
87
|
+
return {
|
|
88
|
+
"record": to_jsonable(record),
|
|
89
|
+
"final_answer": record.result.final_answer,
|
|
90
|
+
"evidence": to_jsonable(record.result.evidence),
|
|
91
|
+
"scored": False,
|
|
92
|
+
"benchmark_score": None,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def catalog() -> dict[str, Any]:
|
|
97
|
+
registry = AlgorithmRegistry.default()
|
|
98
|
+
return {
|
|
99
|
+
"methods": [m.to_dict() for m in registry.list_methods()],
|
|
100
|
+
"benchmarks": [to_jsonable(b) for b in BenchmarkRegistry.default().list()],
|
|
101
|
+
"note": "Inventory is not a claim that every method/dataset/model combination is supported or tested.",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _state(value: Any) -> FrozenAgentState:
|
|
106
|
+
if not isinstance(value, dict):
|
|
107
|
+
raise ValidationError("state must be an object")
|
|
108
|
+
allowed = {
|
|
109
|
+
"query",
|
|
110
|
+
"task_id",
|
|
111
|
+
"instruction",
|
|
112
|
+
"context",
|
|
113
|
+
"messages",
|
|
114
|
+
"tool_names",
|
|
115
|
+
"metadata",
|
|
116
|
+
"budget",
|
|
117
|
+
}
|
|
118
|
+
if value.keys() - allowed:
|
|
119
|
+
raise ValidationError("unknown state fields")
|
|
120
|
+
for key in ("query", "task_id", "instruction"):
|
|
121
|
+
if key in value and not isinstance(value[key], str):
|
|
122
|
+
raise ValidationError(f"{key} must be a string")
|
|
123
|
+
for key in ("context", "messages", "tool_names"):
|
|
124
|
+
if key in value and not isinstance(value[key], list):
|
|
125
|
+
raise ValidationError(f"{key} must be an array")
|
|
126
|
+
for key in ("metadata", "budget"):
|
|
127
|
+
if key in value and not isinstance(value[key], dict):
|
|
128
|
+
raise ValidationError(f"{key} must be an object")
|
|
129
|
+
try:
|
|
130
|
+
return FrozenAgentState(
|
|
131
|
+
**{
|
|
132
|
+
**value,
|
|
133
|
+
"context": tuple(ContextBlock(**x) for x in value.get("context", [])),
|
|
134
|
+
"messages": tuple(Message(**x) for x in value.get("messages", [])),
|
|
135
|
+
"budget": ExecutionBudget(**value.get("budget", {})),
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
except (TypeError, AttributeError) as exc:
|
|
139
|
+
raise ValidationError("invalid nested state fields") from exc
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class ToolkitService:
|
|
143
|
+
"""Operator-owned factories and contexts; clients cannot inject Python code.
|
|
144
|
+
|
|
145
|
+
All Python profiles remain available through create_intervention. HTTP only
|
|
146
|
+
exposes explicitly configured patch profiles. Construct a fresh context per
|
|
147
|
+
request; do not share a mutable budget ledger between calls.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(
|
|
151
|
+
self,
|
|
152
|
+
factories: Mapping[str, Callable[[], Any]] | None = None,
|
|
153
|
+
context_factory: Callable[[int], InterventionContext] | None = None,
|
|
154
|
+
):
|
|
155
|
+
self.factories = dict(
|
|
156
|
+
factories
|
|
157
|
+
if factories is not None
|
|
158
|
+
else {
|
|
159
|
+
"baseline.no_op": NoOpIntervention,
|
|
160
|
+
"plan.plan_and_solve.ps_prompt": lambda: create_intervention(
|
|
161
|
+
"plan.plan_and_solve.ps_prompt"
|
|
162
|
+
),
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
self.context_factory = context_factory or (lambda seed: InterventionContext(seed=seed))
|
|
166
|
+
|
|
167
|
+
def execute(self, body: Any) -> dict[str, Any]:
|
|
168
|
+
if not isinstance(body, dict) or body.keys() - {"release_id", "state", "seed"}:
|
|
169
|
+
raise ValidationError("expected release_id, state and optional seed")
|
|
170
|
+
rid = body.get("release_id")
|
|
171
|
+
if not isinstance(rid, str):
|
|
172
|
+
raise ValidationError("release_id must be a string")
|
|
173
|
+
if rid not in self.factories:
|
|
174
|
+
raise AlgorithmUnavailableError("profile is not configured on this server")
|
|
175
|
+
seed = body.get("seed", 0)
|
|
176
|
+
if isinstance(seed, bool) or not isinstance(seed, int) or seed < 0:
|
|
177
|
+
raise ValidationError("seed must be a non-negative integer")
|
|
178
|
+
state = _state(body.get("state"))
|
|
179
|
+
return run_intervention(self.factories[rid](), state, self.context_factory(seed))
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def make_server(service: ToolkitService, *, token: str, port: int = 8765) -> ThreadingHTTPServer:
|
|
183
|
+
"""Authenticated loopback-only development service, not a public web host.
|
|
184
|
+
|
|
185
|
+
Bounded request bodies and one active execution. Put a production ASGI
|
|
186
|
+
deployment with TLS, quotas and job persistence in front for shared use.
|
|
187
|
+
Client timeouts do not cancel running model calls; there is no auto retry.
|
|
188
|
+
"""
|
|
189
|
+
if not isinstance(token, str) or len(token) < 24 or not token.isascii():
|
|
190
|
+
raise ValueError("API token must be at least 24 ASCII characters")
|
|
191
|
+
lock = threading.Lock()
|
|
192
|
+
|
|
193
|
+
class Handler(BaseHTTPRequestHandler):
|
|
194
|
+
def setup(self):
|
|
195
|
+
super().setup()
|
|
196
|
+
self.connection.settimeout(30)
|
|
197
|
+
|
|
198
|
+
def log_message(self, *_args):
|
|
199
|
+
pass # Never log bearer tokens, user prompts or upstream errors.
|
|
200
|
+
|
|
201
|
+
def send_json(self, status: int, value: Any):
|
|
202
|
+
data = json.dumps(value, allow_nan=False).encode()
|
|
203
|
+
self.send_response(status)
|
|
204
|
+
self.send_header("Content-Type", "application/json")
|
|
205
|
+
self.send_header("Content-Length", str(len(data)))
|
|
206
|
+
self.send_header("Cache-Control", "no-store")
|
|
207
|
+
self.send_header("X-Content-Type-Options", "nosniff")
|
|
208
|
+
self.end_headers()
|
|
209
|
+
self.wfile.write(data)
|
|
210
|
+
|
|
211
|
+
def authorized(self):
|
|
212
|
+
given = self.headers.get("Authorization", "").encode()
|
|
213
|
+
if not hmac.compare_digest(given, ("Bearer " + token).encode()):
|
|
214
|
+
self.send_json(
|
|
215
|
+
401,
|
|
216
|
+
{"error": {"code": "unauthorized", "message": "A valid API token is required"}},
|
|
217
|
+
)
|
|
218
|
+
return False
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
def do_GET(self):
|
|
222
|
+
if not self.authorized():
|
|
223
|
+
return
|
|
224
|
+
if self.path == "/v1/catalog":
|
|
225
|
+
self.send_json(
|
|
226
|
+
200, {"data": {**catalog(), "configured_releases": sorted(service.factories)}}
|
|
227
|
+
)
|
|
228
|
+
elif self.path == "/v1/health":
|
|
229
|
+
self.send_json(200, {"data": {"status": "ok", "protocol": "agentinputlab.v1"}})
|
|
230
|
+
else:
|
|
231
|
+
self.send_json(404, {"error": {"code": "not_found", "message": "Unknown resource"}})
|
|
232
|
+
|
|
233
|
+
def do_POST(self):
|
|
234
|
+
if not self.authorized():
|
|
235
|
+
return
|
|
236
|
+
if self.path != "/v1/interventions":
|
|
237
|
+
self.send_json(404, {"error": {"code": "not_found", "message": "Unknown resource"}})
|
|
238
|
+
return
|
|
239
|
+
if self.headers.get("Transfer-Encoding"):
|
|
240
|
+
self.send_json(
|
|
241
|
+
400,
|
|
242
|
+
{
|
|
243
|
+
"error": {
|
|
244
|
+
"code": "invalid_body",
|
|
245
|
+
"message": "Chunked bodies are unsupported",
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
)
|
|
249
|
+
return
|
|
250
|
+
try:
|
|
251
|
+
length = int(self.headers.get("Content-Length", "-1"))
|
|
252
|
+
except ValueError:
|
|
253
|
+
length = -1
|
|
254
|
+
if length < 0 or length > 1024 * 1024:
|
|
255
|
+
self.send_json(
|
|
256
|
+
413 if length > 0 else 411,
|
|
257
|
+
{
|
|
258
|
+
"error": {
|
|
259
|
+
"code": "body_size",
|
|
260
|
+
"message": "A body of at most 1 MiB is required",
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
return
|
|
265
|
+
if self.headers.get_content_type() != "application/json":
|
|
266
|
+
self.send_json(
|
|
267
|
+
415, {"error": {"code": "content_type", "message": "Use application/json"}}
|
|
268
|
+
)
|
|
269
|
+
return
|
|
270
|
+
if not lock.acquire(blocking=False):
|
|
271
|
+
self.send_json(
|
|
272
|
+
429,
|
|
273
|
+
{
|
|
274
|
+
"error": {
|
|
275
|
+
"code": "busy",
|
|
276
|
+
"message": "An execution is already active; no attempt started",
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
)
|
|
280
|
+
return
|
|
281
|
+
try:
|
|
282
|
+
body = json.loads(
|
|
283
|
+
self.rfile.read(length),
|
|
284
|
+
parse_constant=lambda _: (_ for _ in ()).throw(ValueError()),
|
|
285
|
+
)
|
|
286
|
+
result = service.execute(body)
|
|
287
|
+
self.send_json(200, {"data": result})
|
|
288
|
+
except (json.JSONDecodeError, UnicodeDecodeError, ValueError) as exc:
|
|
289
|
+
# Output errors inherit ValueError through ValidationError.
|
|
290
|
+
code = (
|
|
291
|
+
"algorithm_output"
|
|
292
|
+
if isinstance(exc, AlgorithmOutputError)
|
|
293
|
+
else "invalid_request"
|
|
294
|
+
)
|
|
295
|
+
self.send_json(
|
|
296
|
+
422 if code == "algorithm_output" else 400,
|
|
297
|
+
{
|
|
298
|
+
"error": {
|
|
299
|
+
"code": code,
|
|
300
|
+
"message": "Invalid output"
|
|
301
|
+
if code == "algorithm_output"
|
|
302
|
+
else "Invalid request schema",
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
)
|
|
306
|
+
except (AlgorithmUnavailableError, CapabilityError, RegistryError):
|
|
307
|
+
self.send_json(
|
|
308
|
+
422,
|
|
309
|
+
{
|
|
310
|
+
"error": {
|
|
311
|
+
"code": "unavailable",
|
|
312
|
+
"message": "Required profile dependencies are not configured",
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
)
|
|
316
|
+
except BudgetExceededError:
|
|
317
|
+
self.send_json(
|
|
318
|
+
422,
|
|
319
|
+
{
|
|
320
|
+
"error": {
|
|
321
|
+
"code": "budget_exhausted",
|
|
322
|
+
"message": "Execution budget exhausted",
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
)
|
|
326
|
+
except AgentInputLabError:
|
|
327
|
+
self.send_json(
|
|
328
|
+
422,
|
|
329
|
+
{
|
|
330
|
+
"error": {
|
|
331
|
+
"code": "execution_failed",
|
|
332
|
+
"message": "Execution rejected; no score was fabricated",
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
)
|
|
336
|
+
except Exception:
|
|
337
|
+
self.send_json(
|
|
338
|
+
500,
|
|
339
|
+
{
|
|
340
|
+
"error": {
|
|
341
|
+
"code": "internal_error",
|
|
342
|
+
"message": "Execution failed; inspect the operator environment",
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
)
|
|
346
|
+
finally:
|
|
347
|
+
lock.release()
|
|
348
|
+
|
|
349
|
+
return ThreadingHTTPServer(("127.0.0.1", port), Handler)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def main() -> None:
|
|
353
|
+
parser = argparse.ArgumentParser(description="Authenticated loopback AgentInputLab SDK service")
|
|
354
|
+
parser.add_argument("--port", type=int, default=8765)
|
|
355
|
+
args = parser.parse_args()
|
|
356
|
+
server = make_server(
|
|
357
|
+
ToolkitService(), token=os.environ.get("AGENTINPUTLAB_API_TOKEN", ""), port=args.port
|
|
358
|
+
)
|
|
359
|
+
try:
|
|
360
|
+
server.serve_forever()
|
|
361
|
+
except KeyboardInterrupt:
|
|
362
|
+
pass
|
|
363
|
+
finally:
|
|
364
|
+
server.server_close()
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
if __name__ == "__main__":
|
|
368
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Artifact schemas and checksum-verified local storage."""
|
|
2
|
+
|
|
3
|
+
from .json_file import load_pinned_json_mapping
|
|
4
|
+
from .manifest import AcquisitionPolicy, ArtifactKind, ArtifactManifest, UpstreamLock
|
|
5
|
+
from .store import ArtifactStore, sha256_file
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AcquisitionPolicy",
|
|
9
|
+
"ArtifactKind",
|
|
10
|
+
"ArtifactManifest",
|
|
11
|
+
"ArtifactStore",
|
|
12
|
+
"UpstreamLock",
|
|
13
|
+
"load_pinned_json_mapping",
|
|
14
|
+
"sha256_file",
|
|
15
|
+
]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Bounded checksum-pinned JSON loading without pickle or executable imports."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import math
|
|
8
|
+
import os
|
|
9
|
+
import stat
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from agentinputlab.contracts.errors import ArtifactError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _unique_object(pairs):
|
|
17
|
+
result = {}
|
|
18
|
+
for key, value in pairs:
|
|
19
|
+
if key in result:
|
|
20
|
+
raise ValueError("duplicate JSON object keys")
|
|
21
|
+
result[key] = value
|
|
22
|
+
return result
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _finite_json(_value):
|
|
26
|
+
raise ValueError("non-finite JSON numbers")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _finite_float(value):
|
|
30
|
+
number = float(value)
|
|
31
|
+
if not math.isfinite(number):
|
|
32
|
+
raise ValueError("non-finite JSON numbers")
|
|
33
|
+
return number
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def load_pinned_json_mapping(
|
|
37
|
+
path: str | Path,
|
|
38
|
+
*,
|
|
39
|
+
expected_sha256: str,
|
|
40
|
+
max_bytes: int = 4 * 1024 * 1024,
|
|
41
|
+
) -> dict[str, Any]:
|
|
42
|
+
"""Read one regular UTF-8 JSON object under a caller-trusted byte checksum.
|
|
43
|
+
|
|
44
|
+
The checksum must come from a trusted receipt, not be computed from an
|
|
45
|
+
untrusted file immediately before loading. It proves byte identity, not
|
|
46
|
+
authorship, source-license permission, or the truth of recorded results.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
if (
|
|
50
|
+
not isinstance(expected_sha256, str)
|
|
51
|
+
or len(expected_sha256) != 64
|
|
52
|
+
or any(c not in "0123456789abcdef" for c in expected_sha256.lower())
|
|
53
|
+
):
|
|
54
|
+
raise ArtifactError("expected_sha256 must be a SHA-256 hex digest")
|
|
55
|
+
if type(max_bytes) is not int or max_bytes < 1:
|
|
56
|
+
raise ArtifactError("artifact JSON max_bytes must be a positive integer")
|
|
57
|
+
source = Path(path).expanduser()
|
|
58
|
+
read_failed = False
|
|
59
|
+
try:
|
|
60
|
+
before = source.lstat()
|
|
61
|
+
if not stat.S_ISREG(before.st_mode) or before.st_size > max_bytes:
|
|
62
|
+
raise ArtifactError("artifact JSON must be a bounded regular file, not a symlink")
|
|
63
|
+
descriptor = os.open(
|
|
64
|
+
source, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0)
|
|
65
|
+
)
|
|
66
|
+
with os.fdopen(descriptor, "rb") as handle:
|
|
67
|
+
opened = os.fstat(handle.fileno())
|
|
68
|
+
if not stat.S_ISREG(opened.st_mode) or (before.st_dev, before.st_ino) != (
|
|
69
|
+
opened.st_dev,
|
|
70
|
+
opened.st_ino,
|
|
71
|
+
):
|
|
72
|
+
raise ArtifactError("artifact JSON file changed while opening")
|
|
73
|
+
raw = handle.read(max_bytes + 1)
|
|
74
|
+
after = os.fstat(handle.fileno())
|
|
75
|
+
if (opened.st_size, opened.st_mtime_ns, opened.st_ctime_ns) != (
|
|
76
|
+
after.st_size,
|
|
77
|
+
after.st_mtime_ns,
|
|
78
|
+
after.st_ctime_ns,
|
|
79
|
+
):
|
|
80
|
+
raise ArtifactError("artifact JSON file changed while reading")
|
|
81
|
+
except OSError:
|
|
82
|
+
read_failed = True
|
|
83
|
+
if read_failed:
|
|
84
|
+
raise ArtifactError("artifact JSON file is unavailable")
|
|
85
|
+
if len(raw) > max_bytes:
|
|
86
|
+
raise ArtifactError("artifact JSON exceeds max_bytes")
|
|
87
|
+
if hashlib.sha256(raw).hexdigest() != expected_sha256.lower():
|
|
88
|
+
raise ArtifactError("artifact JSON checksum mismatch")
|
|
89
|
+
invalid_json = False
|
|
90
|
+
value = None
|
|
91
|
+
try:
|
|
92
|
+
value = json.loads(
|
|
93
|
+
raw.decode("utf-8"),
|
|
94
|
+
object_pairs_hook=_unique_object,
|
|
95
|
+
parse_constant=_finite_json,
|
|
96
|
+
parse_float=_finite_float,
|
|
97
|
+
)
|
|
98
|
+
except (UnicodeError, ValueError, RecursionError):
|
|
99
|
+
invalid_json = True
|
|
100
|
+
if invalid_json or not isinstance(value, dict):
|
|
101
|
+
raise ArtifactError(
|
|
102
|
+
"artifact must contain a valid UTF-8 JSON object with unique keys and finite values"
|
|
103
|
+
)
|
|
104
|
+
return value
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Artifact and upstream-source identity schemas."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from agentinputlab.contracts.enums import StrEnum
|
|
11
|
+
from agentinputlab.contracts.errors import ValidationError
|
|
12
|
+
from agentinputlab.contracts.serialization import freeze_mapping
|
|
13
|
+
|
|
14
|
+
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
15
|
+
_GIT_SHA = re.compile(r"^[0-9a-f]{40}$")
|
|
16
|
+
_SAFE_SEGMENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ArtifactKind(StrEnum):
|
|
20
|
+
DATASET = "dataset"
|
|
21
|
+
MODEL = "model"
|
|
22
|
+
INDEX = "index"
|
|
23
|
+
CACHE = "cache"
|
|
24
|
+
FEATURE = "feature"
|
|
25
|
+
PROMPT = "prompt"
|
|
26
|
+
TRACE = "trace"
|
|
27
|
+
RECIPE_OUTPUT = "recipe_output"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AcquisitionPolicy(StrEnum):
|
|
31
|
+
BUNDLED = "bundled"
|
|
32
|
+
DOWNLOAD = "download"
|
|
33
|
+
USER_PROVIDED = "user_provided"
|
|
34
|
+
GATED = "gated"
|
|
35
|
+
EXTERNAL_MOUNT = "external_mount"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True, slots=True)
|
|
39
|
+
class UpstreamLock:
|
|
40
|
+
repository: str
|
|
41
|
+
commit: str
|
|
42
|
+
license_id: str
|
|
43
|
+
vendoring_allowed: bool
|
|
44
|
+
subpath: str = ""
|
|
45
|
+
branch: str = ""
|
|
46
|
+
|
|
47
|
+
def __post_init__(self) -> None:
|
|
48
|
+
if self.commit and not _GIT_SHA.fullmatch(self.commit):
|
|
49
|
+
raise ValidationError("upstream git commit must be a full 40-character SHA")
|
|
50
|
+
if self.license_id == "NOASSERTION" and self.vendoring_allowed:
|
|
51
|
+
raise ValidationError("NOASSERTION source cannot be marked vendoring_allowed")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True, slots=True)
|
|
55
|
+
class ArtifactManifest:
|
|
56
|
+
artifact_id: str
|
|
57
|
+
kind: ArtifactKind
|
|
58
|
+
revision: str
|
|
59
|
+
sha256: str
|
|
60
|
+
acquisition: AcquisitionPolicy
|
|
61
|
+
license_id: str
|
|
62
|
+
source_uri: str = ""
|
|
63
|
+
size_bytes: int | None = None
|
|
64
|
+
lineage: Mapping[str, str] = field(default_factory=dict)
|
|
65
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
66
|
+
|
|
67
|
+
def __post_init__(self) -> None:
|
|
68
|
+
if not _SAFE_SEGMENT.fullmatch(self.artifact_id) or ".." in self.artifact_id:
|
|
69
|
+
raise ValidationError("artifact_id must be a flat safe identifier")
|
|
70
|
+
if not _SAFE_SEGMENT.fullmatch(self.revision) or ".." in self.revision:
|
|
71
|
+
raise ValidationError("artifact revision must be a flat safe identifier")
|
|
72
|
+
if self.sha256 and not _SHA256.fullmatch(self.sha256):
|
|
73
|
+
raise ValidationError("artifact sha256 must contain 64 lowercase hex characters")
|
|
74
|
+
if self.acquisition is AcquisitionPolicy.DOWNLOAD:
|
|
75
|
+
if not self.sha256:
|
|
76
|
+
raise ValidationError("download artifacts require a pinned sha256")
|
|
77
|
+
if not self.source_uri:
|
|
78
|
+
raise ValidationError("download artifacts require a source_uri")
|
|
79
|
+
if self.size_bytes is not None and self.size_bytes < 0:
|
|
80
|
+
raise ValidationError("artifact size must be non-negative")
|
|
81
|
+
lineage = dict(self.lineage)
|
|
82
|
+
if any(
|
|
83
|
+
not isinstance(key, str)
|
|
84
|
+
or not key.strip()
|
|
85
|
+
or not isinstance(value, str)
|
|
86
|
+
or not value.strip()
|
|
87
|
+
for key, value in lineage.items()
|
|
88
|
+
):
|
|
89
|
+
raise ValidationError("artifact lineage requires non-empty string keys and values")
|
|
90
|
+
try:
|
|
91
|
+
frozen_lineage = freeze_mapping(lineage)
|
|
92
|
+
frozen_metadata = freeze_mapping(self.metadata)
|
|
93
|
+
except TypeError as exc:
|
|
94
|
+
raise ValidationError(
|
|
95
|
+
f"artifact metadata must be canonically serializable: {exc}"
|
|
96
|
+
) from exc
|
|
97
|
+
object.__setattr__(self, "lineage", frozen_lineage)
|
|
98
|
+
object.__setattr__(self, "metadata", frozen_metadata)
|