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
workers/acon/driver.py
ADDED
|
@@ -0,0 +1,1859 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""JSONL worker for the pinned microsoft/acon context optimizers.
|
|
3
|
+
|
|
4
|
+
The worker is intentionally a thin execution boundary. It validates the
|
|
5
|
+
checkout and prompt artifacts, then directly instantiates and calls upstream
|
|
6
|
+
``HistoryOptimizer.process`` or ``ObservationOptimizer.process``. It does not
|
|
7
|
+
copy or reimplement either algorithm's prompt construction or output parser.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import contextlib
|
|
13
|
+
import hashlib
|
|
14
|
+
import importlib
|
|
15
|
+
import importlib.metadata
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import tempfile
|
|
22
|
+
import urllib.error
|
|
23
|
+
import urllib.parse
|
|
24
|
+
import urllib.request
|
|
25
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any, TextIO
|
|
29
|
+
|
|
30
|
+
UPSTREAM_COMMIT = "d63f9ae18959dc7215ff62899c94c5e8c56847ae"
|
|
31
|
+
MANIFEST_SCHEMA = "agentinputlab.acon.compatibility.v1"
|
|
32
|
+
HISTORY_PROTOCOL = "agentinputlab.acon.history.v2"
|
|
33
|
+
OBSERVATION_PROTOCOL = "agentinputlab.acon.observation.v2"
|
|
34
|
+
HISTORY_RELEASE = "curate.acon.history_runtime"
|
|
35
|
+
OBSERVATION_RELEASE = "curate.acon.observation_runtime"
|
|
36
|
+
DEFAULT_MANIFEST = Path(__file__).with_name("compatibility_manifest.json")
|
|
37
|
+
|
|
38
|
+
CHECKOUT_ENV = "AGENTINPUTLAB_ACON_CHECKOUT"
|
|
39
|
+
UPSTREAM_ROOT_ENV = "AGENTINPUTLAB_UPSTREAM_ROOT"
|
|
40
|
+
BASE_URL_ENV = "AGENTINPUTLAB_ACON_OPENAI_BASE_URL"
|
|
41
|
+
API_KEY_ENV = "AGENTINPUTLAB_ACON_API_KEY"
|
|
42
|
+
SERVING_MODEL_ENV = "AGENTINPUTLAB_ACON_OPTIMIZER_MODEL"
|
|
43
|
+
OPTIMIZER_REVISION_ENV = "AGENTINPUTLAB_ACON_OPTIMIZER_MODEL_REVISION"
|
|
44
|
+
TARGET_REVISION_ENV = "AGENTINPUTLAB_ACON_TARGET_MODEL_REVISION"
|
|
45
|
+
TOKENIZER_REVISION_ENV = "AGENTINPUTLAB_ACON_TOKENIZER_REVISION"
|
|
46
|
+
TEMPERATURE_ENV = "AGENTINPUTLAB_ACON_TEMPERATURE"
|
|
47
|
+
TIMEOUT_ENV = "AGENTINPUTLAB_ACON_TIMEOUT_SECONDS"
|
|
48
|
+
MAX_TOKENS_ENV = "AGENTINPUTLAB_ACON_MAX_TOKENS"
|
|
49
|
+
EXTRA_BODY_ENV = "AGENTINPUTLAB_ACON_EXTRA_BODY_JSON"
|
|
50
|
+
|
|
51
|
+
MAX_REQUEST_BYTES = 8 * 1024 * 1024
|
|
52
|
+
MAX_TEXT_CHARS = 4 * 1024 * 1024
|
|
53
|
+
MAX_RESPONSE_BYTES = 8 * 1024 * 1024
|
|
54
|
+
_HEX_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
55
|
+
_TOP_FIELDS = frozenset(
|
|
56
|
+
{
|
|
57
|
+
"method_id",
|
|
58
|
+
"release_id",
|
|
59
|
+
"upstream_commit",
|
|
60
|
+
"state_hash",
|
|
61
|
+
"payload",
|
|
62
|
+
"dependency_fingerprints",
|
|
63
|
+
"minimum_reservation",
|
|
64
|
+
"protocol_version",
|
|
65
|
+
"request_id",
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
_ARTIFACT_FIELDS = frozenset(
|
|
69
|
+
{
|
|
70
|
+
"optimizer_model_revision",
|
|
71
|
+
"target_model_revision",
|
|
72
|
+
"tokenizer_revision",
|
|
73
|
+
"prompt_artifact_hash",
|
|
74
|
+
"guideline_artifact_hash",
|
|
75
|
+
"optimizer_deployment_fingerprint_hash",
|
|
76
|
+
"target_backend_fingerprint_hash",
|
|
77
|
+
"tokenizer_fingerprint_hash",
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
_HISTORY_PAYLOAD_FIELDS = frozenset(
|
|
81
|
+
{
|
|
82
|
+
"task",
|
|
83
|
+
"raw_messages",
|
|
84
|
+
"history_text",
|
|
85
|
+
"history_text_hash",
|
|
86
|
+
"counted_material_hash",
|
|
87
|
+
"previous_history_summary",
|
|
88
|
+
"preserved_message_indices",
|
|
89
|
+
"preserved_messages",
|
|
90
|
+
"controls",
|
|
91
|
+
"artifacts",
|
|
92
|
+
"seed",
|
|
93
|
+
"phase",
|
|
94
|
+
"decision_token",
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
_OBSERVATION_PAYLOAD_FIELDS = frozenset(
|
|
98
|
+
{
|
|
99
|
+
"task",
|
|
100
|
+
"observation",
|
|
101
|
+
"source_context_id",
|
|
102
|
+
"history_text",
|
|
103
|
+
"history_text_hash",
|
|
104
|
+
"raw_messages",
|
|
105
|
+
"opt_args",
|
|
106
|
+
"opt_args_hash",
|
|
107
|
+
"controls",
|
|
108
|
+
"artifacts",
|
|
109
|
+
"seed",
|
|
110
|
+
"phase",
|
|
111
|
+
"decision_token",
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class DriverError(RuntimeError):
|
|
117
|
+
"""A stable, secret-free worker failure."""
|
|
118
|
+
|
|
119
|
+
def __init__(
|
|
120
|
+
self,
|
|
121
|
+
code: str,
|
|
122
|
+
message: str,
|
|
123
|
+
*,
|
|
124
|
+
consumed: bool = False,
|
|
125
|
+
usage: tuple[int, int, int] | None = None,
|
|
126
|
+
) -> None:
|
|
127
|
+
super().__init__(message)
|
|
128
|
+
self.code = code
|
|
129
|
+
self.public_message = message
|
|
130
|
+
self.consumed = consumed
|
|
131
|
+
self.usage = usage
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass(frozen=True, slots=True)
|
|
135
|
+
class Checkout:
|
|
136
|
+
root: Path
|
|
137
|
+
manifest: Mapping[str, Any]
|
|
138
|
+
manifest_sha256: str
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True, slots=True)
|
|
142
|
+
class ParsedRequest:
|
|
143
|
+
request_id: str
|
|
144
|
+
protocol_version: str
|
|
145
|
+
mode: str
|
|
146
|
+
payload: Mapping[str, Any]
|
|
147
|
+
state_hash: str
|
|
148
|
+
dependencies: Mapping[str, Any]
|
|
149
|
+
reservation: Mapping[str, Any]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True, slots=True)
|
|
153
|
+
class PromptProfile:
|
|
154
|
+
profile_id: str
|
|
155
|
+
mode: str
|
|
156
|
+
prompt_dir: Path
|
|
157
|
+
system_template: str
|
|
158
|
+
user_template: str
|
|
159
|
+
prompt_artifact_hash: str
|
|
160
|
+
guideline_artifact_hash: str
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
LLMFactory = Callable[..., Any]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class _NoRedirectHandler(urllib.request.HTTPRedirectHandler):
|
|
167
|
+
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
168
|
+
del req, fp, code, msg, headers, newurl
|
|
169
|
+
raise DriverError(
|
|
170
|
+
"MODEL_REDIRECT_REFUSED",
|
|
171
|
+
"optimizer endpoint redirects are disabled",
|
|
172
|
+
consumed=True,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class OpenAICompatibleLLM:
|
|
177
|
+
"""The small ``generate`` seam expected by pinned ACON.
|
|
178
|
+
|
|
179
|
+
Both hosted OpenAI-compatible APIs and local vLLM servers use the same
|
|
180
|
+
request shape. Provider-specific retries, hidden defaults, and fallback
|
|
181
|
+
models are deliberately excluded from this reproducibility boundary.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
def __init__(
|
|
185
|
+
self,
|
|
186
|
+
*,
|
|
187
|
+
base_url: str,
|
|
188
|
+
api_key: str,
|
|
189
|
+
model: str,
|
|
190
|
+
seed: int,
|
|
191
|
+
default_temperature: float,
|
|
192
|
+
timeout_seconds: float,
|
|
193
|
+
max_tokens: int | None,
|
|
194
|
+
extra_body: Mapping[str, Any] | None = None,
|
|
195
|
+
) -> None:
|
|
196
|
+
self.endpoint = _chat_completions_endpoint(base_url)
|
|
197
|
+
if api_key and urllib.parse.urlsplit(self.endpoint).scheme != "https":
|
|
198
|
+
raise DriverError(
|
|
199
|
+
"MODEL_CONFIGURATION_INVALID",
|
|
200
|
+
"optimizer API keys may only be sent over HTTPS",
|
|
201
|
+
)
|
|
202
|
+
self.api_key = api_key
|
|
203
|
+
self.model = model
|
|
204
|
+
self.seed = seed
|
|
205
|
+
self.default_temperature = default_temperature
|
|
206
|
+
self.timeout_seconds = timeout_seconds
|
|
207
|
+
self.max_tokens = max_tokens
|
|
208
|
+
try:
|
|
209
|
+
normalized_extra = json.loads(_canonical_json(dict(extra_body or {})))
|
|
210
|
+
except (TypeError, ValueError) as exc:
|
|
211
|
+
raise DriverError(
|
|
212
|
+
"MODEL_CONFIGURATION_INVALID",
|
|
213
|
+
"optimizer extra body must be canonical JSON",
|
|
214
|
+
) from exc
|
|
215
|
+
reserved = set(normalized_extra) & {
|
|
216
|
+
"model",
|
|
217
|
+
"messages",
|
|
218
|
+
"temperature",
|
|
219
|
+
"seed",
|
|
220
|
+
"max_tokens",
|
|
221
|
+
}
|
|
222
|
+
if reserved:
|
|
223
|
+
raise DriverError(
|
|
224
|
+
"MODEL_CONFIGURATION_INVALID",
|
|
225
|
+
"optimizer extra body cannot override protocol fields",
|
|
226
|
+
)
|
|
227
|
+
self.extra_body = normalized_extra
|
|
228
|
+
self.system_message = ""
|
|
229
|
+
self.total_model_calls = 0
|
|
230
|
+
self.total_input_tokens = 0
|
|
231
|
+
self.total_output_tokens = 0
|
|
232
|
+
self._opener = urllib.request.build_opener(_NoRedirectHandler())
|
|
233
|
+
|
|
234
|
+
def fingerprint(self) -> Mapping[str, Any]:
|
|
235
|
+
identity = {
|
|
236
|
+
"transport": "openai_compatible_no_redirect_v1",
|
|
237
|
+
"endpoint": self.endpoint,
|
|
238
|
+
"model": self.model,
|
|
239
|
+
"temperature": self.default_temperature,
|
|
240
|
+
"max_tokens": self.max_tokens,
|
|
241
|
+
}
|
|
242
|
+
if self.extra_body:
|
|
243
|
+
identity["extra_body_hash"] = _content_hash(self.extra_body)
|
|
244
|
+
return identity
|
|
245
|
+
|
|
246
|
+
def generate(self, prompt: str, temperature: float | None = None) -> str:
|
|
247
|
+
if not isinstance(prompt, str):
|
|
248
|
+
raise DriverError(
|
|
249
|
+
"UPSTREAM_API_MISMATCH",
|
|
250
|
+
"pinned ACON v1 produced a non-text optimizer prompt",
|
|
251
|
+
)
|
|
252
|
+
messages: list[dict[str, str]] = []
|
|
253
|
+
if self.system_message:
|
|
254
|
+
messages.append({"role": "system", "content": self.system_message})
|
|
255
|
+
messages.append({"role": "user", "content": prompt})
|
|
256
|
+
body: dict[str, Any] = {
|
|
257
|
+
**self.extra_body,
|
|
258
|
+
"model": self.model,
|
|
259
|
+
"messages": messages,
|
|
260
|
+
"temperature": (
|
|
261
|
+
self.default_temperature if temperature is None else float(temperature)
|
|
262
|
+
),
|
|
263
|
+
"seed": self.seed,
|
|
264
|
+
}
|
|
265
|
+
if self.max_tokens is not None:
|
|
266
|
+
body["max_tokens"] = self.max_tokens
|
|
267
|
+
encoded = _canonical_json(body).encode("utf-8")
|
|
268
|
+
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
|
269
|
+
if self.api_key:
|
|
270
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
271
|
+
request = urllib.request.Request(
|
|
272
|
+
self.endpoint,
|
|
273
|
+
data=encoded,
|
|
274
|
+
headers=headers,
|
|
275
|
+
method="POST",
|
|
276
|
+
)
|
|
277
|
+
self.total_model_calls += 1
|
|
278
|
+
try:
|
|
279
|
+
with self._opener.open(request, timeout=self.timeout_seconds) as response:
|
|
280
|
+
raw = response.read(MAX_RESPONSE_BYTES + 1)
|
|
281
|
+
except DriverError:
|
|
282
|
+
raise
|
|
283
|
+
except urllib.error.HTTPError as exc:
|
|
284
|
+
raise DriverError(
|
|
285
|
+
"MODEL_REQUEST_FAILED",
|
|
286
|
+
f"optimizer endpoint returned HTTP {exc.code}",
|
|
287
|
+
consumed=True,
|
|
288
|
+
usage=_usage(self),
|
|
289
|
+
) from exc
|
|
290
|
+
except (urllib.error.URLError, TimeoutError, OSError) as exc:
|
|
291
|
+
raise DriverError(
|
|
292
|
+
"MODEL_REQUEST_FAILED",
|
|
293
|
+
"optimizer endpoint request failed",
|
|
294
|
+
consumed=True,
|
|
295
|
+
usage=_usage(self),
|
|
296
|
+
) from exc
|
|
297
|
+
if len(raw) > MAX_RESPONSE_BYTES:
|
|
298
|
+
raise DriverError(
|
|
299
|
+
"MODEL_RESPONSE_TOO_LARGE",
|
|
300
|
+
"optimizer endpoint response exceeded its byte limit",
|
|
301
|
+
consumed=True,
|
|
302
|
+
usage=_usage(self),
|
|
303
|
+
)
|
|
304
|
+
try:
|
|
305
|
+
value = json.loads(raw)
|
|
306
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
307
|
+
raise DriverError(
|
|
308
|
+
"MODEL_RESPONSE_INVALID",
|
|
309
|
+
"optimizer endpoint returned invalid JSON",
|
|
310
|
+
consumed=True,
|
|
311
|
+
usage=_usage(self),
|
|
312
|
+
) from exc
|
|
313
|
+
input_tokens, output_tokens = _parse_model_usage(value)
|
|
314
|
+
self.total_input_tokens += input_tokens
|
|
315
|
+
self.total_output_tokens += output_tokens
|
|
316
|
+
text, _, _ = _parse_model_response(value, expected_model=self.model)
|
|
317
|
+
return text
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class ACONDriver:
|
|
321
|
+
"""Validate one request and execute the corresponding upstream optimizer."""
|
|
322
|
+
|
|
323
|
+
def __init__(
|
|
324
|
+
self,
|
|
325
|
+
*,
|
|
326
|
+
manifest_path: Path = DEFAULT_MANIFEST,
|
|
327
|
+
environment: Mapping[str, str] | None = None,
|
|
328
|
+
llm_factory: LLMFactory | None = None,
|
|
329
|
+
) -> None:
|
|
330
|
+
self.manifest_path = Path(manifest_path)
|
|
331
|
+
self.environment = dict(os.environ if environment is None else environment)
|
|
332
|
+
self.llm_factory = llm_factory
|
|
333
|
+
|
|
334
|
+
def smoke_upstream_core(self) -> Mapping[str, str]:
|
|
335
|
+
checkout = self._checkout()
|
|
336
|
+
with (
|
|
337
|
+
_upstream_import_scope(checkout.root),
|
|
338
|
+
contextlib.redirect_stdout(sys.stderr),
|
|
339
|
+
):
|
|
340
|
+
history_class, observation_class = _load_upstream_classes()
|
|
341
|
+
return {
|
|
342
|
+
"upstream_commit": UPSTREAM_COMMIT,
|
|
343
|
+
"history_symbol": (f"{history_class.__module__}.{history_class.__name__}"),
|
|
344
|
+
"observation_symbol": (f"{observation_class.__module__}.{observation_class.__name__}"),
|
|
345
|
+
"manifest_sha256": checkout.manifest_sha256,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
def run(self, value: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
349
|
+
request = _parse_request(value)
|
|
350
|
+
checkout = self._checkout()
|
|
351
|
+
artifacts = _parse_artifacts(request.payload.get("artifacts"))
|
|
352
|
+
profile = _select_profile(checkout, request.mode, artifacts)
|
|
353
|
+
_validate_bound_revisions(self.environment, artifacts)
|
|
354
|
+
target_hash = _validate_target_dependency(request.dependencies, artifacts)
|
|
355
|
+
temperature = _environment_float(
|
|
356
|
+
self.environment,
|
|
357
|
+
TEMPERATURE_ENV,
|
|
358
|
+
default=0.0,
|
|
359
|
+
minimum=0.0,
|
|
360
|
+
maximum=2.0,
|
|
361
|
+
)
|
|
362
|
+
llm = self._make_llm(artifacts, request.payload["seed"], temperature)
|
|
363
|
+
optimizer_hash = _fingerprint_hash(llm, "optimizer LLM")
|
|
364
|
+
if optimizer_hash != artifacts["optimizer_deployment_fingerprint_hash"]:
|
|
365
|
+
raise DriverError(
|
|
366
|
+
"ARTIFACT_REVISION_MISMATCH",
|
|
367
|
+
"optimizer deployment fingerprint disagrees with the request",
|
|
368
|
+
)
|
|
369
|
+
execute = request.payload["phase"] == "execute"
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
with (
|
|
373
|
+
_upstream_import_scope(checkout.root),
|
|
374
|
+
contextlib.redirect_stdout(sys.stderr),
|
|
375
|
+
):
|
|
376
|
+
history_class, observation_class = _load_upstream_classes()
|
|
377
|
+
runner = _run_history if request.mode == "history" else _run_observation
|
|
378
|
+
optimizer_class = history_class if request.mode == "history" else observation_class
|
|
379
|
+
decision_payload = runner(
|
|
380
|
+
request.payload,
|
|
381
|
+
profile,
|
|
382
|
+
optimizer_class,
|
|
383
|
+
llm,
|
|
384
|
+
temperature,
|
|
385
|
+
execute=False,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
tokenizer_hash = str(decision_payload.pop("_tokenizer_fingerprint_hash"))
|
|
389
|
+
if tokenizer_hash != artifacts["tokenizer_fingerprint_hash"]:
|
|
390
|
+
raise DriverError(
|
|
391
|
+
"ARTIFACT_REVISION_MISMATCH",
|
|
392
|
+
"runtime tokenizer fingerprint disagrees with the request",
|
|
393
|
+
)
|
|
394
|
+
attestation = {
|
|
395
|
+
"optimizer_deployment_fingerprint_hash": optimizer_hash,
|
|
396
|
+
"target_backend_fingerprint_hash": target_hash,
|
|
397
|
+
"tokenizer_fingerprint_hash": tokenizer_hash,
|
|
398
|
+
}
|
|
399
|
+
decision_token = _decision_token(
|
|
400
|
+
request,
|
|
401
|
+
profile=profile,
|
|
402
|
+
manifest_sha256=checkout.manifest_sha256,
|
|
403
|
+
attestation=attestation,
|
|
404
|
+
payload=decision_payload,
|
|
405
|
+
)
|
|
406
|
+
if execute and request.payload["decision_token"] != decision_token:
|
|
407
|
+
raise DriverError(
|
|
408
|
+
"DECISION_TOKEN_MISMATCH",
|
|
409
|
+
"ACON execute request does not match its preflight decision",
|
|
410
|
+
)
|
|
411
|
+
if execute:
|
|
412
|
+
payload = runner(
|
|
413
|
+
request.payload,
|
|
414
|
+
profile,
|
|
415
|
+
optimizer_class,
|
|
416
|
+
llm,
|
|
417
|
+
temperature,
|
|
418
|
+
execute=True,
|
|
419
|
+
)
|
|
420
|
+
execute_tokenizer_hash = str(payload.pop("_tokenizer_fingerprint_hash"))
|
|
421
|
+
if execute_tokenizer_hash != tokenizer_hash or any(
|
|
422
|
+
payload[key] != decision_payload[key]
|
|
423
|
+
for key in ("raw_input_tokens", "threshold_triggered", "applied")
|
|
424
|
+
):
|
|
425
|
+
raise DriverError(
|
|
426
|
+
"UPSTREAM_SEMANTICS_MISMATCH",
|
|
427
|
+
"ACON decision changed between preflight and execution",
|
|
428
|
+
)
|
|
429
|
+
else:
|
|
430
|
+
payload = decision_payload
|
|
431
|
+
except DriverError as exc:
|
|
432
|
+
calls = _usage_or_none(llm)
|
|
433
|
+
if calls is not None and calls[0] > 0 and exc.usage is None:
|
|
434
|
+
exc.consumed = True
|
|
435
|
+
exc.usage = calls
|
|
436
|
+
raise
|
|
437
|
+
|
|
438
|
+
calls, input_tokens, output_tokens = _usage(llm)
|
|
439
|
+
expected_calls = 1 if execute else 0
|
|
440
|
+
if calls != expected_calls:
|
|
441
|
+
raise DriverError(
|
|
442
|
+
"USAGE_ACCOUNTING_MISMATCH",
|
|
443
|
+
"optimizer model-call accounting disagrees with the ACON branch",
|
|
444
|
+
)
|
|
445
|
+
payload = {
|
|
446
|
+
**payload,
|
|
447
|
+
"decision_token": decision_token,
|
|
448
|
+
"runtime_attestation": attestation,
|
|
449
|
+
"profile_id": profile.profile_id,
|
|
450
|
+
"compatibility_manifest_sha256": checkout.manifest_sha256,
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
"status": "ok",
|
|
454
|
+
"request_id": request.request_id,
|
|
455
|
+
"upstream_commit": UPSTREAM_COMMIT,
|
|
456
|
+
"protocol_version": request.protocol_version,
|
|
457
|
+
"payload": payload,
|
|
458
|
+
"usage": {
|
|
459
|
+
"model_calls": calls,
|
|
460
|
+
"retrieval_calls": 0,
|
|
461
|
+
"input_tokens": input_tokens,
|
|
462
|
+
"output_tokens": output_tokens,
|
|
463
|
+
},
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
def _checkout(self) -> Checkout:
|
|
467
|
+
root = _resolve_checkout(self.environment)
|
|
468
|
+
manifest, manifest_hash = _load_manifest(self.manifest_path)
|
|
469
|
+
_verify_checkout(root, manifest)
|
|
470
|
+
return Checkout(root=root, manifest=manifest, manifest_sha256=manifest_hash)
|
|
471
|
+
|
|
472
|
+
def _make_llm(
|
|
473
|
+
self,
|
|
474
|
+
artifacts: Mapping[str, str],
|
|
475
|
+
seed: int,
|
|
476
|
+
temperature: float,
|
|
477
|
+
) -> Any:
|
|
478
|
+
model = self.environment.get(SERVING_MODEL_ENV, "").strip()
|
|
479
|
+
if not model:
|
|
480
|
+
model = artifacts["optimizer_model_revision"]
|
|
481
|
+
if self.llm_factory is not None:
|
|
482
|
+
try:
|
|
483
|
+
llm = self.llm_factory(
|
|
484
|
+
model=model,
|
|
485
|
+
seed=seed,
|
|
486
|
+
environment=dict(self.environment),
|
|
487
|
+
default_temperature=temperature,
|
|
488
|
+
)
|
|
489
|
+
except DriverError:
|
|
490
|
+
raise
|
|
491
|
+
except Exception as exc:
|
|
492
|
+
raise DriverError(
|
|
493
|
+
"MODEL_CONFIGURATION_INVALID",
|
|
494
|
+
"optimizer LLM factory failed",
|
|
495
|
+
) from exc
|
|
496
|
+
_validate_llm_seam(llm)
|
|
497
|
+
return llm
|
|
498
|
+
base_url = self.environment.get(BASE_URL_ENV, "").strip()
|
|
499
|
+
if not base_url:
|
|
500
|
+
raise DriverError(
|
|
501
|
+
"MODEL_CONFIGURATION_MISSING",
|
|
502
|
+
f"{BASE_URL_ENV} is required for ACON runtime attestation",
|
|
503
|
+
)
|
|
504
|
+
timeout = _environment_float(
|
|
505
|
+
self.environment,
|
|
506
|
+
TIMEOUT_ENV,
|
|
507
|
+
default=120.0,
|
|
508
|
+
minimum=0.1,
|
|
509
|
+
maximum=3600.0,
|
|
510
|
+
)
|
|
511
|
+
max_tokens = _environment_optional_positive_int(self.environment, MAX_TOKENS_ENV)
|
|
512
|
+
extra_body = _environment_json_mapping(self.environment, EXTRA_BODY_ENV)
|
|
513
|
+
return OpenAICompatibleLLM(
|
|
514
|
+
base_url=base_url,
|
|
515
|
+
api_key=self.environment.get(API_KEY_ENV, ""),
|
|
516
|
+
model=model,
|
|
517
|
+
seed=seed,
|
|
518
|
+
default_temperature=temperature,
|
|
519
|
+
timeout_seconds=timeout,
|
|
520
|
+
max_tokens=max_tokens,
|
|
521
|
+
extra_body=extra_body,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def _parse_request(value: Mapping[str, Any]) -> ParsedRequest:
|
|
526
|
+
if not isinstance(value, Mapping):
|
|
527
|
+
raise DriverError("INVALID_REQUEST", "worker request must be a JSON object")
|
|
528
|
+
if set(value) != _TOP_FIELDS:
|
|
529
|
+
raise DriverError("INVALID_REQUEST", "worker request fields do not match schema")
|
|
530
|
+
if value.get("method_id") != "acon":
|
|
531
|
+
raise DriverError("INVALID_REQUEST", "worker request method_id must be acon")
|
|
532
|
+
if value.get("upstream_commit") != UPSTREAM_COMMIT:
|
|
533
|
+
raise DriverError("REVISION_MISMATCH", "request upstream revision is not pinned")
|
|
534
|
+
release = value.get("release_id")
|
|
535
|
+
protocol = value.get("protocol_version")
|
|
536
|
+
if release == HISTORY_RELEASE and protocol == HISTORY_PROTOCOL:
|
|
537
|
+
mode = "history"
|
|
538
|
+
payload_fields = _HISTORY_PAYLOAD_FIELDS
|
|
539
|
+
elif release == OBSERVATION_RELEASE and protocol == OBSERVATION_PROTOCOL:
|
|
540
|
+
mode = "observation"
|
|
541
|
+
payload_fields = _OBSERVATION_PAYLOAD_FIELDS
|
|
542
|
+
else:
|
|
543
|
+
raise DriverError(
|
|
544
|
+
"SCHEMA_MISMATCH",
|
|
545
|
+
"request release and protocol do not identify a supported ACON v2 mode",
|
|
546
|
+
)
|
|
547
|
+
request_id = _sha_text(value.get("request_id"), "request_id")
|
|
548
|
+
state_hash = _sha_text(value.get("state_hash"), "state_hash")
|
|
549
|
+
payload = value.get("payload")
|
|
550
|
+
if not isinstance(payload, Mapping) or set(payload) != payload_fields:
|
|
551
|
+
raise DriverError("INVALID_REQUEST", "ACON payload fields do not match schema")
|
|
552
|
+
dependencies = value.get("dependency_fingerprints")
|
|
553
|
+
reservation = value.get("minimum_reservation")
|
|
554
|
+
if not isinstance(dependencies, Mapping) or not isinstance(reservation, Mapping):
|
|
555
|
+
raise DriverError("INVALID_REQUEST", "worker dependency metadata must be mappings")
|
|
556
|
+
if set(dependencies) != {"target_backend"} or not isinstance(
|
|
557
|
+
dependencies.get("target_backend"), Mapping
|
|
558
|
+
):
|
|
559
|
+
raise DriverError("INVALID_REQUEST", "ACON requires one target_backend fingerprint")
|
|
560
|
+
phase = payload.get("phase")
|
|
561
|
+
decision_token = payload.get("decision_token")
|
|
562
|
+
if phase == "preflight":
|
|
563
|
+
if decision_token != "" or dict(reservation):
|
|
564
|
+
raise DriverError("INVALID_REQUEST", "ACON preflight cannot reserve or carry a token")
|
|
565
|
+
elif phase == "execute":
|
|
566
|
+
_sha_text(decision_token, "decision_token")
|
|
567
|
+
if dict(reservation) != {"model_calls": 1}:
|
|
568
|
+
raise DriverError("INVALID_REQUEST", "ACON execute must reserve one model call")
|
|
569
|
+
else:
|
|
570
|
+
raise DriverError("INVALID_REQUEST", "ACON phase must be preflight or execute")
|
|
571
|
+
try:
|
|
572
|
+
expected_id = _content_hash(
|
|
573
|
+
{
|
|
574
|
+
"method_id": value["method_id"],
|
|
575
|
+
"release_id": release,
|
|
576
|
+
"upstream_commit": value["upstream_commit"],
|
|
577
|
+
"state_hash": value["state_hash"],
|
|
578
|
+
"payload": payload,
|
|
579
|
+
"dependency_fingerprints": dependencies,
|
|
580
|
+
"minimum_reservation": reservation,
|
|
581
|
+
"protocol_version": protocol,
|
|
582
|
+
}
|
|
583
|
+
)
|
|
584
|
+
except (TypeError, ValueError) as exc:
|
|
585
|
+
raise DriverError("INVALID_REQUEST", "worker request is not canonical JSON") from exc
|
|
586
|
+
if request_id != expected_id:
|
|
587
|
+
raise DriverError("REQUEST_ID_MISMATCH", "worker request_id is not canonical")
|
|
588
|
+
normalized = json.loads(_canonical_json(payload))
|
|
589
|
+
normalized_dependencies = json.loads(_canonical_json(dependencies))
|
|
590
|
+
normalized_reservation = json.loads(_canonical_json(reservation))
|
|
591
|
+
if mode == "history":
|
|
592
|
+
_validate_history_payload(normalized)
|
|
593
|
+
else:
|
|
594
|
+
_validate_observation_payload(normalized)
|
|
595
|
+
return ParsedRequest(
|
|
596
|
+
request_id=request_id,
|
|
597
|
+
protocol_version=str(protocol),
|
|
598
|
+
mode=mode,
|
|
599
|
+
payload=normalized,
|
|
600
|
+
state_hash=state_hash,
|
|
601
|
+
dependencies=normalized_dependencies,
|
|
602
|
+
reservation=normalized_reservation,
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def _validate_history_payload(payload: Mapping[str, Any]) -> None:
|
|
607
|
+
_bounded_text(payload.get("task"), "task", allow_empty=False)
|
|
608
|
+
messages = _messages(payload.get("raw_messages"))
|
|
609
|
+
history = _bounded_text(payload.get("history_text"), "history_text")
|
|
610
|
+
previous = _bounded_text(
|
|
611
|
+
payload.get("previous_history_summary"),
|
|
612
|
+
"previous_history_summary",
|
|
613
|
+
)
|
|
614
|
+
if _content_hash(history) != _sha_text(payload.get("history_text_hash"), "history_text_hash"):
|
|
615
|
+
raise DriverError("SOURCE_MAPPING_MISMATCH", "history_text_hash does not match history")
|
|
616
|
+
controls = _history_controls(payload.get("controls"))
|
|
617
|
+
indices = _integer_sequence(payload.get("preserved_message_indices"), "preserved indices")
|
|
618
|
+
if len(indices) != len(set(indices)) or any(index >= len(messages) for index in indices):
|
|
619
|
+
raise DriverError("SOURCE_MAPPING_MISMATCH", "preserved message indices are invalid")
|
|
620
|
+
preserved = _message_sequence(payload.get("preserved_messages"), "preserved_messages")
|
|
621
|
+
expected_preserved = [messages[index] for index in indices]
|
|
622
|
+
if _canonical_json(preserved) != _canonical_json(expected_preserved):
|
|
623
|
+
raise DriverError("SOURCE_MAPPING_MISMATCH", "preserved messages do not match indices")
|
|
624
|
+
expected_indices = _preserved_turn_indices(messages, controls["preserve_last_k_turns"])
|
|
625
|
+
if tuple(indices) != expected_indices:
|
|
626
|
+
raise DriverError(
|
|
627
|
+
"SOURCE_MAPPING_MISMATCH", "preserved turn selection is not upstream-faithful"
|
|
628
|
+
)
|
|
629
|
+
expected_history = _history_material(messages, preserved)
|
|
630
|
+
if history != expected_history:
|
|
631
|
+
raise DriverError(
|
|
632
|
+
"SOURCE_MAPPING_MISMATCH", "history text is not derived from raw messages"
|
|
633
|
+
)
|
|
634
|
+
counted = f"{previous}\n{history}" if previous else history
|
|
635
|
+
counted_hash = _sha_text(payload.get("counted_material_hash"), "counted_material_hash")
|
|
636
|
+
if _content_hash(counted) != counted_hash:
|
|
637
|
+
raise DriverError("SOURCE_MAPPING_MISMATCH", "counted material hash does not match input")
|
|
638
|
+
_parse_artifacts(payload.get("artifacts"))
|
|
639
|
+
_seed(payload.get("seed"))
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def _validate_observation_payload(payload: Mapping[str, Any]) -> None:
|
|
643
|
+
_bounded_text(payload.get("task"), "task", allow_empty=False)
|
|
644
|
+
observation = _bounded_text(payload.get("observation"), "observation", allow_empty=False)
|
|
645
|
+
del observation
|
|
646
|
+
_bounded_text(payload.get("source_context_id"), "source_context_id", allow_empty=False)
|
|
647
|
+
history = _bounded_text(payload.get("history_text"), "history_text")
|
|
648
|
+
history_hash = _sha_text(payload.get("history_text_hash"), "history_text_hash")
|
|
649
|
+
if _content_hash(history) != history_hash:
|
|
650
|
+
raise DriverError("SOURCE_MAPPING_MISMATCH", "history_text_hash does not match history")
|
|
651
|
+
messages = _messages(payload.get("raw_messages"))
|
|
652
|
+
controls = _observation_controls(payload.get("controls"))
|
|
653
|
+
if not controls["add_previous_history_summary"]:
|
|
654
|
+
expected_history = _history_text(messages)
|
|
655
|
+
if history != expected_history:
|
|
656
|
+
raise DriverError(
|
|
657
|
+
"SOURCE_MAPPING_MISMATCH",
|
|
658
|
+
"observation history text is not derived from raw messages",
|
|
659
|
+
)
|
|
660
|
+
opt_args = payload.get("opt_args")
|
|
661
|
+
if not isinstance(opt_args, Mapping):
|
|
662
|
+
raise DriverError("INVALID_REQUEST", "opt_args must be a JSON object")
|
|
663
|
+
collisions = set(opt_args) & {"task", "observation", "history"}
|
|
664
|
+
if collisions:
|
|
665
|
+
raise DriverError(
|
|
666
|
+
"INVALID_REQUEST",
|
|
667
|
+
"opt_args cannot override reserved upstream inputs: " + ", ".join(sorted(collisions)),
|
|
668
|
+
)
|
|
669
|
+
try:
|
|
670
|
+
opt_hash = _content_hash(opt_args)
|
|
671
|
+
except (TypeError, ValueError) as exc:
|
|
672
|
+
raise DriverError("INVALID_REQUEST", "opt_args must be canonical JSON") from exc
|
|
673
|
+
if opt_hash != _sha_text(payload.get("opt_args_hash"), "opt_args_hash"):
|
|
674
|
+
raise DriverError("SOURCE_MAPPING_MISMATCH", "opt_args_hash does not match opt_args")
|
|
675
|
+
_parse_artifacts(payload.get("artifacts"))
|
|
676
|
+
_seed(payload.get("seed"))
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def _run_history(
|
|
680
|
+
payload: Mapping[str, Any],
|
|
681
|
+
profile: PromptProfile,
|
|
682
|
+
optimizer_class: type[Any],
|
|
683
|
+
llm: Any,
|
|
684
|
+
temperature: float,
|
|
685
|
+
*,
|
|
686
|
+
execute: bool,
|
|
687
|
+
) -> Mapping[str, Any]:
|
|
688
|
+
controls = _history_controls(payload["controls"])
|
|
689
|
+
config = {
|
|
690
|
+
"history_prompt_dir": str(profile.prompt_dir),
|
|
691
|
+
"prompts": {
|
|
692
|
+
"prompt_system": profile.system_template,
|
|
693
|
+
"prompt_history_user": profile.user_template,
|
|
694
|
+
},
|
|
695
|
+
"history_summarization_threshold": controls["summarization_threshold"],
|
|
696
|
+
"model": payload["artifacts"]["optimizer_model_revision"],
|
|
697
|
+
"temperature": temperature,
|
|
698
|
+
"use_llmlingua": False,
|
|
699
|
+
}
|
|
700
|
+
optimizer = _construct_optimizer(optimizer_class, config, llm, "HistoryOptimizer")
|
|
701
|
+
counted = (
|
|
702
|
+
f"{payload['previous_history_summary']}\n{payload['history_text']}"
|
|
703
|
+
if payload["previous_history_summary"]
|
|
704
|
+
else payload["history_text"]
|
|
705
|
+
)
|
|
706
|
+
raw_tokens = _token_count(optimizer, counted)
|
|
707
|
+
try:
|
|
708
|
+
threshold_triggered = optimizer.check_summarization_needed(
|
|
709
|
+
payload["history_text"],
|
|
710
|
+
payload["previous_history_summary"],
|
|
711
|
+
)
|
|
712
|
+
except Exception as exc:
|
|
713
|
+
raise DriverError(
|
|
714
|
+
"UPSTREAM_EXECUTION_FAILED",
|
|
715
|
+
"upstream HistoryOptimizer threshold check failed",
|
|
716
|
+
) from exc
|
|
717
|
+
if not isinstance(threshold_triggered, bool):
|
|
718
|
+
raise DriverError(
|
|
719
|
+
"UPSTREAM_API_MISMATCH",
|
|
720
|
+
"upstream HistoryOptimizer returned a non-boolean threshold decision",
|
|
721
|
+
)
|
|
722
|
+
expected_threshold = (
|
|
723
|
+
controls["summarization_threshold"] == -1
|
|
724
|
+
or raw_tokens > controls["summarization_threshold"]
|
|
725
|
+
)
|
|
726
|
+
if threshold_triggered is not expected_threshold:
|
|
727
|
+
raise DriverError(
|
|
728
|
+
"UPSTREAM_SEMANTICS_MISMATCH",
|
|
729
|
+
"upstream HistoryOptimizer threshold semantics changed",
|
|
730
|
+
)
|
|
731
|
+
interval_ready = (
|
|
732
|
+
controls["summary_interval"] == -1
|
|
733
|
+
or len(payload["raw_messages"]) // 2 >= controls["summary_interval"]
|
|
734
|
+
)
|
|
735
|
+
applied = bool(payload["history_text"].strip()) and threshold_triggered and interval_ready
|
|
736
|
+
if execute and not applied:
|
|
737
|
+
raise DriverError(
|
|
738
|
+
"DECISION_NOT_EXECUTABLE", "ACON history preflight selected a skip branch"
|
|
739
|
+
)
|
|
740
|
+
summary = ""
|
|
741
|
+
raw_response = ""
|
|
742
|
+
if applied and execute:
|
|
743
|
+
before = _optimizer_history_size(optimizer)
|
|
744
|
+
try:
|
|
745
|
+
summary = optimizer.process(
|
|
746
|
+
task=payload["task"],
|
|
747
|
+
history=payload["history_text"],
|
|
748
|
+
prev_history_summary=payload["previous_history_summary"],
|
|
749
|
+
raw_history=payload["raw_messages"],
|
|
750
|
+
opt_args=None,
|
|
751
|
+
)
|
|
752
|
+
except DriverError:
|
|
753
|
+
raise
|
|
754
|
+
except ModuleNotFoundError as exc:
|
|
755
|
+
raise _dependency_error(exc) from exc
|
|
756
|
+
except Exception as exc:
|
|
757
|
+
raise DriverError(
|
|
758
|
+
"UPSTREAM_EXECUTION_FAILED",
|
|
759
|
+
"upstream HistoryOptimizer.process failed",
|
|
760
|
+
) from exc
|
|
761
|
+
summary = _upstream_text(summary, "history summary")
|
|
762
|
+
raw_response = _raw_optimizer_response(optimizer, before)
|
|
763
|
+
if not summary.strip() or not raw_response.strip():
|
|
764
|
+
raise DriverError(
|
|
765
|
+
"UPSTREAM_OUTPUT_INVALID",
|
|
766
|
+
"upstream HistoryOptimizer returned an empty applied result",
|
|
767
|
+
)
|
|
768
|
+
return {
|
|
769
|
+
**dict(payload["artifacts"]),
|
|
770
|
+
"counted_material_hash": payload["counted_material_hash"],
|
|
771
|
+
"raw_input_tokens": raw_tokens,
|
|
772
|
+
"threshold_triggered": threshold_triggered,
|
|
773
|
+
"applied": applied,
|
|
774
|
+
"summary": summary,
|
|
775
|
+
"raw_response": raw_response,
|
|
776
|
+
"_tokenizer_fingerprint_hash": _tokenizer_fingerprint_hash(optimizer),
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def _run_observation(
|
|
781
|
+
payload: Mapping[str, Any],
|
|
782
|
+
profile: PromptProfile,
|
|
783
|
+
optimizer_class: type[Any],
|
|
784
|
+
llm: Any,
|
|
785
|
+
temperature: float,
|
|
786
|
+
*,
|
|
787
|
+
execute: bool,
|
|
788
|
+
) -> Mapping[str, Any]:
|
|
789
|
+
controls = _observation_controls(payload["controls"])
|
|
790
|
+
config = {
|
|
791
|
+
"obs_prompt_dir": str(profile.prompt_dir),
|
|
792
|
+
"prompts": {
|
|
793
|
+
"prompt_system": profile.system_template,
|
|
794
|
+
"prompt_user": profile.user_template,
|
|
795
|
+
},
|
|
796
|
+
"obs_summarization_threshold": controls["summarization_threshold"],
|
|
797
|
+
"model": payload["artifacts"]["optimizer_model_revision"],
|
|
798
|
+
"temperature": temperature,
|
|
799
|
+
"use_llmlingua": False,
|
|
800
|
+
}
|
|
801
|
+
optimizer = _construct_optimizer(optimizer_class, config, llm, "ObservationOptimizer")
|
|
802
|
+
raw_tokens = _token_count(optimizer, payload["observation"])
|
|
803
|
+
try:
|
|
804
|
+
threshold_triggered = optimizer.check_summarization_needed(payload["observation"])
|
|
805
|
+
except Exception as exc:
|
|
806
|
+
raise DriverError(
|
|
807
|
+
"UPSTREAM_EXECUTION_FAILED",
|
|
808
|
+
"upstream ObservationOptimizer threshold check failed",
|
|
809
|
+
) from exc
|
|
810
|
+
if not isinstance(threshold_triggered, bool):
|
|
811
|
+
raise DriverError(
|
|
812
|
+
"UPSTREAM_API_MISMATCH",
|
|
813
|
+
"upstream ObservationOptimizer returned a non-boolean threshold decision",
|
|
814
|
+
)
|
|
815
|
+
expected_threshold = (
|
|
816
|
+
controls["summarization_threshold"] == -1
|
|
817
|
+
or raw_tokens > controls["summarization_threshold"]
|
|
818
|
+
)
|
|
819
|
+
if threshold_triggered is not expected_threshold:
|
|
820
|
+
raise DriverError(
|
|
821
|
+
"UPSTREAM_SEMANTICS_MISMATCH",
|
|
822
|
+
"upstream ObservationOptimizer threshold semantics changed",
|
|
823
|
+
)
|
|
824
|
+
optimized = payload["observation"]
|
|
825
|
+
raw_response = ""
|
|
826
|
+
if execute and not threshold_triggered:
|
|
827
|
+
raise DriverError(
|
|
828
|
+
"DECISION_NOT_EXECUTABLE",
|
|
829
|
+
"ACON observation preflight selected a skip branch",
|
|
830
|
+
)
|
|
831
|
+
if threshold_triggered and execute:
|
|
832
|
+
before = _optimizer_history_size(optimizer)
|
|
833
|
+
try:
|
|
834
|
+
optimized = optimizer.process(
|
|
835
|
+
task=payload["task"],
|
|
836
|
+
observation=payload["observation"],
|
|
837
|
+
history=payload["history_text"],
|
|
838
|
+
raw_history=payload["raw_messages"],
|
|
839
|
+
opt_args=payload["opt_args"],
|
|
840
|
+
)
|
|
841
|
+
except DriverError:
|
|
842
|
+
raise
|
|
843
|
+
except ModuleNotFoundError as exc:
|
|
844
|
+
raise _dependency_error(exc) from exc
|
|
845
|
+
except Exception as exc:
|
|
846
|
+
raise DriverError(
|
|
847
|
+
"UPSTREAM_EXECUTION_FAILED",
|
|
848
|
+
"upstream ObservationOptimizer.process failed",
|
|
849
|
+
) from exc
|
|
850
|
+
optimized = _upstream_text(optimized, "optimized observation")
|
|
851
|
+
raw_response = _raw_optimizer_response(optimizer, before)
|
|
852
|
+
if not optimized.strip() or not raw_response.strip():
|
|
853
|
+
raise DriverError(
|
|
854
|
+
"UPSTREAM_OUTPUT_INVALID",
|
|
855
|
+
"upstream ObservationOptimizer returned an empty applied result",
|
|
856
|
+
)
|
|
857
|
+
return {
|
|
858
|
+
**dict(payload["artifacts"]),
|
|
859
|
+
"source_context_id": payload["source_context_id"],
|
|
860
|
+
"history_text_hash": payload["history_text_hash"],
|
|
861
|
+
"opt_args_hash": payload["opt_args_hash"],
|
|
862
|
+
"raw_input_tokens": raw_tokens,
|
|
863
|
+
"threshold_triggered": threshold_triggered,
|
|
864
|
+
"applied": threshold_triggered,
|
|
865
|
+
"optimized_observation": optimized,
|
|
866
|
+
"raw_response": raw_response,
|
|
867
|
+
"_tokenizer_fingerprint_hash": _tokenizer_fingerprint_hash(optimizer),
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
def _construct_optimizer(
|
|
872
|
+
optimizer_class: type[Any],
|
|
873
|
+
config: Mapping[str, Any],
|
|
874
|
+
llm: Any,
|
|
875
|
+
label: str,
|
|
876
|
+
) -> Any:
|
|
877
|
+
try:
|
|
878
|
+
optimizer = optimizer_class(dict(config), debug_mode=False, llm=llm)
|
|
879
|
+
except ModuleNotFoundError as exc:
|
|
880
|
+
raise _dependency_error(exc) from exc
|
|
881
|
+
except Exception as exc:
|
|
882
|
+
raise DriverError(
|
|
883
|
+
"UPSTREAM_BOOT_FAILED",
|
|
884
|
+
f"upstream {label} construction failed",
|
|
885
|
+
) from exc
|
|
886
|
+
if not callable(getattr(optimizer, "process", None)):
|
|
887
|
+
raise DriverError("UPSTREAM_API_MISMATCH", f"upstream {label}.process is unavailable")
|
|
888
|
+
if not callable(getattr(optimizer, "count_tokens", None)) or not callable(
|
|
889
|
+
getattr(optimizer, "check_summarization_needed", None)
|
|
890
|
+
):
|
|
891
|
+
raise DriverError("UPSTREAM_API_MISMATCH", f"upstream {label} helpers are unavailable")
|
|
892
|
+
try:
|
|
893
|
+
llm.system_message = optimizer.system_message
|
|
894
|
+
except Exception as exc:
|
|
895
|
+
raise DriverError(
|
|
896
|
+
"UPSTREAM_API_MISMATCH",
|
|
897
|
+
"optimizer LLM seam rejected the upstream system message",
|
|
898
|
+
) from exc
|
|
899
|
+
return optimizer
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
def _token_count(optimizer: Any, text: str) -> int:
|
|
903
|
+
try:
|
|
904
|
+
value = optimizer.count_tokens(text)
|
|
905
|
+
except Exception as exc:
|
|
906
|
+
raise DriverError(
|
|
907
|
+
"UPSTREAM_EXECUTION_FAILED",
|
|
908
|
+
"upstream ACON token counting failed",
|
|
909
|
+
) from exc
|
|
910
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
911
|
+
raise DriverError(
|
|
912
|
+
"UPSTREAM_API_MISMATCH",
|
|
913
|
+
"upstream ACON returned an invalid token count",
|
|
914
|
+
)
|
|
915
|
+
return value
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
def _optimizer_history_size(optimizer: Any) -> int:
|
|
919
|
+
history = getattr(optimizer, "history", None)
|
|
920
|
+
if not isinstance(history, list):
|
|
921
|
+
raise DriverError(
|
|
922
|
+
"UPSTREAM_API_MISMATCH",
|
|
923
|
+
"upstream optimizer interaction history is unavailable",
|
|
924
|
+
)
|
|
925
|
+
return len(history)
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
def _raw_optimizer_response(optimizer: Any, before: int) -> str:
|
|
929
|
+
history = getattr(optimizer, "history", None)
|
|
930
|
+
if not isinstance(history, list) or len(history) != before + 1:
|
|
931
|
+
raise DriverError(
|
|
932
|
+
"UPSTREAM_API_MISMATCH",
|
|
933
|
+
"upstream optimizer did not record exactly one interaction",
|
|
934
|
+
)
|
|
935
|
+
record = history[-1]
|
|
936
|
+
if (
|
|
937
|
+
isinstance(record, (str, bytes))
|
|
938
|
+
or not isinstance(record, Sequence)
|
|
939
|
+
or len(record) < 3
|
|
940
|
+
or not isinstance(record[2], str)
|
|
941
|
+
):
|
|
942
|
+
raise DriverError(
|
|
943
|
+
"UPSTREAM_API_MISMATCH",
|
|
944
|
+
"upstream optimizer interaction record is incompatible",
|
|
945
|
+
)
|
|
946
|
+
return record[2]
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
def _usage(llm: Any) -> tuple[int, int, int]:
|
|
950
|
+
values = (
|
|
951
|
+
getattr(llm, "total_model_calls", None),
|
|
952
|
+
getattr(llm, "total_input_tokens", None),
|
|
953
|
+
getattr(llm, "total_output_tokens", None),
|
|
954
|
+
)
|
|
955
|
+
if any(isinstance(item, bool) or not isinstance(item, int) or item < 0 for item in values):
|
|
956
|
+
raise DriverError(
|
|
957
|
+
"USAGE_ACCOUNTING_UNAVAILABLE",
|
|
958
|
+
"optimizer LLM seam did not expose non-negative usage counters",
|
|
959
|
+
)
|
|
960
|
+
return values # type: ignore[return-value]
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
def _usage_or_none(llm: Any) -> tuple[int, int, int] | None:
|
|
964
|
+
try:
|
|
965
|
+
return _usage(llm)
|
|
966
|
+
except DriverError:
|
|
967
|
+
return None
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
def _validate_llm_seam(llm: Any) -> None:
|
|
971
|
+
if not callable(getattr(llm, "generate", None)):
|
|
972
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", "optimizer LLM lacks generate()")
|
|
973
|
+
_usage(llm)
|
|
974
|
+
_fingerprint_hash(llm, "optimizer LLM")
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
def _fingerprint_hash(value: Any, label: str) -> str:
|
|
978
|
+
fingerprint = getattr(value, "fingerprint", None)
|
|
979
|
+
if not callable(fingerprint):
|
|
980
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{label} lacks fingerprint()")
|
|
981
|
+
identity = fingerprint()
|
|
982
|
+
if not isinstance(identity, Mapping) or not identity:
|
|
983
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{label} fingerprint is invalid")
|
|
984
|
+
try:
|
|
985
|
+
return _content_hash(identity)
|
|
986
|
+
except (TypeError, ValueError) as exc:
|
|
987
|
+
raise DriverError(
|
|
988
|
+
"MODEL_CONFIGURATION_INVALID", f"{label} fingerprint is not canonical"
|
|
989
|
+
) from exc
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
def _decision_token(
|
|
993
|
+
request: ParsedRequest,
|
|
994
|
+
*,
|
|
995
|
+
profile: PromptProfile,
|
|
996
|
+
manifest_sha256: str,
|
|
997
|
+
attestation: Mapping[str, str],
|
|
998
|
+
payload: Mapping[str, Any],
|
|
999
|
+
) -> str:
|
|
1000
|
+
return _content_hash(
|
|
1001
|
+
{
|
|
1002
|
+
"mode": request.mode,
|
|
1003
|
+
"state_hash": request.state_hash,
|
|
1004
|
+
"payload": {
|
|
1005
|
+
key: item
|
|
1006
|
+
for key, item in request.payload.items()
|
|
1007
|
+
if key not in {"phase", "decision_token"}
|
|
1008
|
+
},
|
|
1009
|
+
"dependencies": request.dependencies,
|
|
1010
|
+
"profile_id": profile.profile_id,
|
|
1011
|
+
"manifest_sha256": manifest_sha256,
|
|
1012
|
+
"runtime_attestation": attestation,
|
|
1013
|
+
"raw_input_tokens": payload["raw_input_tokens"],
|
|
1014
|
+
"threshold_triggered": payload["threshold_triggered"],
|
|
1015
|
+
"applied": payload["applied"],
|
|
1016
|
+
}
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def _validate_target_dependency(
|
|
1021
|
+
dependencies: Mapping[str, Any], artifacts: Mapping[str, str]
|
|
1022
|
+
) -> str:
|
|
1023
|
+
target = dependencies.get("target_backend")
|
|
1024
|
+
try:
|
|
1025
|
+
digest = _content_hash(target)
|
|
1026
|
+
except (TypeError, ValueError) as exc: # pragma: no cover - parser already canonicalizes
|
|
1027
|
+
raise DriverError("INVALID_REQUEST", "target backend fingerprint is not canonical") from exc
|
|
1028
|
+
if digest != artifacts["target_backend_fingerprint_hash"]:
|
|
1029
|
+
raise DriverError(
|
|
1030
|
+
"ARTIFACT_REVISION_MISMATCH",
|
|
1031
|
+
"target backend fingerprint disagrees with the request artifacts",
|
|
1032
|
+
)
|
|
1033
|
+
if artifacts["target_model_revision"] not in _text_leaves(target):
|
|
1034
|
+
raise DriverError(
|
|
1035
|
+
"ARTIFACT_REVISION_MISMATCH",
|
|
1036
|
+
"target model revision is absent from its backend fingerprint",
|
|
1037
|
+
)
|
|
1038
|
+
return digest
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
def _text_leaves(value: Any) -> set[str]:
|
|
1042
|
+
if isinstance(value, str):
|
|
1043
|
+
return {value}
|
|
1044
|
+
if isinstance(value, Mapping):
|
|
1045
|
+
return set().union(*(_text_leaves(item) for item in value.values()), set())
|
|
1046
|
+
if isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
|
|
1047
|
+
return set().union(*(_text_leaves(item) for item in value), set())
|
|
1048
|
+
return set()
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
def _tokenizer_fingerprint_hash(optimizer: Any) -> str:
|
|
1052
|
+
tokenizer = getattr(optimizer, "tokenizer", None)
|
|
1053
|
+
if tokenizer is None or not callable(getattr(tokenizer, "encode", None)):
|
|
1054
|
+
raise DriverError(
|
|
1055
|
+
"TOKENIZER_ATTESTATION_UNAVAILABLE",
|
|
1056
|
+
"pinned ACON tokenizer is unavailable; approximate counting is forbidden",
|
|
1057
|
+
)
|
|
1058
|
+
custom = getattr(tokenizer, "agentinputlab_fingerprint", None)
|
|
1059
|
+
if callable(custom):
|
|
1060
|
+
identity = custom()
|
|
1061
|
+
else:
|
|
1062
|
+
name = getattr(tokenizer, "name", None)
|
|
1063
|
+
if not isinstance(name, str) or not name:
|
|
1064
|
+
raise DriverError("TOKENIZER_ATTESTATION_UNAVAILABLE", "tokenizer name is unavailable")
|
|
1065
|
+
try:
|
|
1066
|
+
version = importlib.metadata.version("tiktoken")
|
|
1067
|
+
probes = [
|
|
1068
|
+
list(tokenizer.encode(text)) for text in ("AgentInputLab", "中文", "line\nbreak")
|
|
1069
|
+
]
|
|
1070
|
+
except Exception as exc:
|
|
1071
|
+
raise DriverError(
|
|
1072
|
+
"TOKENIZER_ATTESTATION_UNAVAILABLE",
|
|
1073
|
+
"tiktoken version or deterministic probe is unavailable",
|
|
1074
|
+
) from exc
|
|
1075
|
+
identity = {
|
|
1076
|
+
"engine": "tiktoken",
|
|
1077
|
+
"version": version,
|
|
1078
|
+
"encoding": name,
|
|
1079
|
+
"probe_hash": _content_hash(probes),
|
|
1080
|
+
}
|
|
1081
|
+
if not isinstance(identity, Mapping) or not identity:
|
|
1082
|
+
raise DriverError("TOKENIZER_ATTESTATION_UNAVAILABLE", "tokenizer fingerprint is invalid")
|
|
1083
|
+
return _content_hash(identity)
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
def _parse_model_response(value: Any, *, expected_model: str) -> tuple[str, int, int]:
|
|
1087
|
+
if not isinstance(value, Mapping):
|
|
1088
|
+
raise DriverError("MODEL_RESPONSE_INVALID", "optimizer response must be an object")
|
|
1089
|
+
response_model = value.get("model")
|
|
1090
|
+
if not isinstance(response_model, str) or response_model != expected_model:
|
|
1091
|
+
raise DriverError(
|
|
1092
|
+
"MODEL_RESPONSE_IDENTITY_MISMATCH",
|
|
1093
|
+
"optimizer response model does not match the configured serving alias",
|
|
1094
|
+
)
|
|
1095
|
+
choices = value.get("choices")
|
|
1096
|
+
if (
|
|
1097
|
+
isinstance(choices, (str, bytes))
|
|
1098
|
+
or not isinstance(choices, Sequence)
|
|
1099
|
+
or len(choices) != 1
|
|
1100
|
+
or not isinstance(choices[0], Mapping)
|
|
1101
|
+
):
|
|
1102
|
+
raise DriverError(
|
|
1103
|
+
"MODEL_RESPONSE_INVALID",
|
|
1104
|
+
"optimizer response must contain exactly one choice",
|
|
1105
|
+
)
|
|
1106
|
+
message = choices[0].get("message")
|
|
1107
|
+
if not isinstance(message, Mapping) or not isinstance(message.get("content"), str):
|
|
1108
|
+
raise DriverError("MODEL_RESPONSE_INVALID", "optimizer response content is missing")
|
|
1109
|
+
text = message["content"]
|
|
1110
|
+
if len(text) > MAX_TEXT_CHARS:
|
|
1111
|
+
raise DriverError("MODEL_RESPONSE_TOO_LARGE", "optimizer text exceeded its size limit")
|
|
1112
|
+
input_tokens, output_tokens = _parse_model_usage(value)
|
|
1113
|
+
return text, input_tokens, output_tokens
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
def _parse_model_usage(value: Any) -> tuple[int, int]:
|
|
1117
|
+
usage = value.get("usage") if isinstance(value, Mapping) else None
|
|
1118
|
+
if not isinstance(usage, Mapping):
|
|
1119
|
+
raise DriverError(
|
|
1120
|
+
"USAGE_ACCOUNTING_UNAVAILABLE",
|
|
1121
|
+
"optimizer endpoint omitted token usage",
|
|
1122
|
+
)
|
|
1123
|
+
counters = (usage.get("prompt_tokens"), usage.get("completion_tokens"))
|
|
1124
|
+
if any(
|
|
1125
|
+
isinstance(counter, bool) or not isinstance(counter, int) or counter < 0
|
|
1126
|
+
for counter in counters
|
|
1127
|
+
):
|
|
1128
|
+
raise DriverError(
|
|
1129
|
+
"USAGE_ACCOUNTING_UNAVAILABLE",
|
|
1130
|
+
"optimizer endpoint returned invalid token usage",
|
|
1131
|
+
)
|
|
1132
|
+
input_tokens, output_tokens = counters
|
|
1133
|
+
return input_tokens, output_tokens
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
def _load_upstream_classes() -> tuple[type[Any], type[Any]]:
|
|
1137
|
+
try:
|
|
1138
|
+
history_module = importlib.import_module("productive_agents.ctxopt.history_optimizer")
|
|
1139
|
+
observation_module = importlib.import_module("productive_agents.ctxopt.obs_optimizer")
|
|
1140
|
+
except ModuleNotFoundError as exc:
|
|
1141
|
+
raise _dependency_error(exc) from exc
|
|
1142
|
+
history_class = getattr(history_module, "HistoryOptimizer", None)
|
|
1143
|
+
observation_class = getattr(observation_module, "ObservationOptimizer", None)
|
|
1144
|
+
if not isinstance(history_class, type) or not isinstance(observation_class, type):
|
|
1145
|
+
raise DriverError(
|
|
1146
|
+
"UPSTREAM_API_MISMATCH",
|
|
1147
|
+
"pinned ACON optimizer classes are unavailable",
|
|
1148
|
+
)
|
|
1149
|
+
return history_class, observation_class
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
@contextlib.contextmanager
|
|
1153
|
+
def _upstream_import_scope(root: Path) -> Iterator[None]:
|
|
1154
|
+
source = str(root / "src")
|
|
1155
|
+
previous_prefix = sys.pycache_prefix
|
|
1156
|
+
previous_dont_write = sys.dont_write_bytecode
|
|
1157
|
+
previous = {
|
|
1158
|
+
name: module
|
|
1159
|
+
for name, module in tuple(sys.modules.items())
|
|
1160
|
+
if name == "productive_agents" or name.startswith("productive_agents.")
|
|
1161
|
+
}
|
|
1162
|
+
for name in previous:
|
|
1163
|
+
sys.modules.pop(name, None)
|
|
1164
|
+
with tempfile.TemporaryDirectory(prefix="agentinputlab-acon-pycache-") as cache:
|
|
1165
|
+
sys.pycache_prefix = cache
|
|
1166
|
+
sys.dont_write_bytecode = True
|
|
1167
|
+
sys.path.insert(0, source)
|
|
1168
|
+
try:
|
|
1169
|
+
yield
|
|
1170
|
+
finally:
|
|
1171
|
+
for name in tuple(sys.modules):
|
|
1172
|
+
if name == "productive_agents" or name.startswith("productive_agents."):
|
|
1173
|
+
sys.modules.pop(name, None)
|
|
1174
|
+
sys.modules.update(previous)
|
|
1175
|
+
sys.pycache_prefix = previous_prefix
|
|
1176
|
+
sys.dont_write_bytecode = previous_dont_write
|
|
1177
|
+
with contextlib.suppress(ValueError):
|
|
1178
|
+
sys.path.remove(source)
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
def _resolve_checkout(environment: Mapping[str, str]) -> Path:
|
|
1182
|
+
explicit = environment.get(CHECKOUT_ENV, "").strip()
|
|
1183
|
+
root_value = explicit or environment.get(UPSTREAM_ROOT_ENV, "").strip()
|
|
1184
|
+
if not root_value:
|
|
1185
|
+
raise DriverError(
|
|
1186
|
+
"UPSTREAM_ROOT_MISSING",
|
|
1187
|
+
f"{CHECKOUT_ENV} or {UPSTREAM_ROOT_ENV} must identify the pinned ACON checkout",
|
|
1188
|
+
)
|
|
1189
|
+
candidate = Path(root_value).expanduser().resolve()
|
|
1190
|
+
if not explicit and (candidate / "acon").is_dir():
|
|
1191
|
+
candidate = (candidate / "acon").resolve()
|
|
1192
|
+
if not candidate.is_dir():
|
|
1193
|
+
raise DriverError("UPSTREAM_ROOT_INVALID", "upstream checkout directory is unavailable")
|
|
1194
|
+
return candidate
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
def _load_manifest(path: Path) -> tuple[Mapping[str, Any], str]:
|
|
1198
|
+
try:
|
|
1199
|
+
raw = path.read_bytes()
|
|
1200
|
+
value = json.loads(raw)
|
|
1201
|
+
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
1202
|
+
raise DriverError("MANIFEST_INVALID", "compatibility manifest is unavailable") from exc
|
|
1203
|
+
if not isinstance(value, Mapping) or value.get("schema_version") != MANIFEST_SCHEMA:
|
|
1204
|
+
raise DriverError("MANIFEST_INVALID", "compatibility manifest schema is invalid")
|
|
1205
|
+
upstream = value.get("upstream")
|
|
1206
|
+
if not isinstance(upstream, Mapping) or upstream.get("commit") != UPSTREAM_COMMIT:
|
|
1207
|
+
raise DriverError("MANIFEST_INVALID", "compatibility manifest revision is invalid")
|
|
1208
|
+
if upstream.get("license_spdx") != "MIT":
|
|
1209
|
+
raise DriverError("MANIFEST_INVALID", "compatibility manifest license is invalid")
|
|
1210
|
+
_sha_text(upstream.get("license_sha256"), "manifest license_sha256", manifest=True)
|
|
1211
|
+
if not isinstance(upstream.get("require_clean_checkout"), bool):
|
|
1212
|
+
raise DriverError("MANIFEST_INVALID", "manifest checkout policy is invalid")
|
|
1213
|
+
minimum_python = upstream.get("minimum_python")
|
|
1214
|
+
if (
|
|
1215
|
+
isinstance(minimum_python, (str, bytes))
|
|
1216
|
+
or not isinstance(minimum_python, Sequence)
|
|
1217
|
+
or len(minimum_python) != 2
|
|
1218
|
+
or any(isinstance(item, bool) or not isinstance(item, int) for item in minimum_python)
|
|
1219
|
+
):
|
|
1220
|
+
raise DriverError("MANIFEST_INVALID", "manifest minimum_python is invalid")
|
|
1221
|
+
if tuple(sys.version_info[:2]) < tuple(minimum_python):
|
|
1222
|
+
raise DriverError(
|
|
1223
|
+
"PYTHON_VERSION_UNSUPPORTED",
|
|
1224
|
+
"pinned ACON requires a newer isolated Python worker environment",
|
|
1225
|
+
)
|
|
1226
|
+
source_files = value.get("source_files")
|
|
1227
|
+
profiles = value.get("prompt_profiles")
|
|
1228
|
+
if not isinstance(source_files, Mapping) or not source_files:
|
|
1229
|
+
raise DriverError("MANIFEST_INVALID", "manifest source_files is invalid")
|
|
1230
|
+
if not isinstance(profiles, Mapping) or not profiles:
|
|
1231
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt_profiles is invalid")
|
|
1232
|
+
for relative, digest in source_files.items():
|
|
1233
|
+
_manifest_path_hash(relative, digest, "source file")
|
|
1234
|
+
modes: set[str] = set()
|
|
1235
|
+
for profile_id, raw_profile in profiles.items():
|
|
1236
|
+
if not isinstance(profile_id, str) or not profile_id:
|
|
1237
|
+
raise DriverError("MANIFEST_INVALID", "manifest profile ID is invalid")
|
|
1238
|
+
profile = _manifest_profile(raw_profile)
|
|
1239
|
+
modes.add(profile["mode"])
|
|
1240
|
+
expected_bundle = _sha256_bytes(_canonical_json(profile["prompt_files"]).encode("utf-8"))
|
|
1241
|
+
if profile["prompt_artifact_hash"] != expected_bundle:
|
|
1242
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt bundle hash is invalid")
|
|
1243
|
+
guideline_path = f"{profile['prompt_dir']}/{profile['user_template']}.jinja"
|
|
1244
|
+
if profile["prompt_files"].get(guideline_path) != profile["guideline_artifact_hash"]:
|
|
1245
|
+
raise DriverError("MANIFEST_INVALID", "manifest guideline hash is invalid")
|
|
1246
|
+
if modes != {"history", "observation"}:
|
|
1247
|
+
raise DriverError("MANIFEST_INVALID", "manifest must contain both ACON optimizer modes")
|
|
1248
|
+
return value, _sha256_bytes(raw)
|
|
1249
|
+
|
|
1250
|
+
|
|
1251
|
+
def _verify_checkout(root: Path, manifest: Mapping[str, Any]) -> None:
|
|
1252
|
+
if _read_git_head(root) != UPSTREAM_COMMIT:
|
|
1253
|
+
raise DriverError("UPSTREAM_REVISION_MISMATCH", "upstream checkout is not pinned")
|
|
1254
|
+
upstream = manifest["upstream"]
|
|
1255
|
+
if _sha256_file(_safe_file(root, "LICENSE"), "LICENSE") != upstream["license_sha256"]:
|
|
1256
|
+
raise DriverError("UPSTREAM_LICENSE_MISMATCH", "upstream LICENSE hash changed")
|
|
1257
|
+
for relative, expected in manifest["source_files"].items():
|
|
1258
|
+
if _sha256_file(_safe_file(root, relative), "source") != expected:
|
|
1259
|
+
raise DriverError("UPSTREAM_SOURCE_MISMATCH", "pinned upstream source hash changed")
|
|
1260
|
+
checked: set[str] = set()
|
|
1261
|
+
for raw_profile in manifest["prompt_profiles"].values():
|
|
1262
|
+
profile = _manifest_profile(raw_profile)
|
|
1263
|
+
for relative, expected in profile["prompt_files"].items():
|
|
1264
|
+
if relative in checked:
|
|
1265
|
+
continue
|
|
1266
|
+
if _sha256_file(_safe_file(root, relative), "prompt") != expected:
|
|
1267
|
+
raise DriverError(
|
|
1268
|
+
"UPSTREAM_PROMPT_MISMATCH",
|
|
1269
|
+
"pinned upstream prompt artifact hash changed",
|
|
1270
|
+
)
|
|
1271
|
+
checked.add(relative)
|
|
1272
|
+
if upstream.get("require_clean_checkout") is True:
|
|
1273
|
+
_verify_clean_checkout(root)
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
def _read_git_head(root: Path) -> str:
|
|
1277
|
+
git_dir = root / ".git"
|
|
1278
|
+
if git_dir.is_file():
|
|
1279
|
+
try:
|
|
1280
|
+
marker = git_dir.read_text(encoding="utf-8").strip()
|
|
1281
|
+
except OSError as exc:
|
|
1282
|
+
raise DriverError(
|
|
1283
|
+
"UPSTREAM_GIT_INVALID", "upstream git metadata is unavailable"
|
|
1284
|
+
) from exc
|
|
1285
|
+
if not marker.startswith("gitdir: "):
|
|
1286
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream gitdir marker is invalid")
|
|
1287
|
+
git_dir = (root / marker[8:]).resolve()
|
|
1288
|
+
if not git_dir.is_dir():
|
|
1289
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream .git metadata is unavailable")
|
|
1290
|
+
try:
|
|
1291
|
+
head = (git_dir / "HEAD").read_text(encoding="utf-8").strip()
|
|
1292
|
+
except OSError as exc:
|
|
1293
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream HEAD is unavailable") from exc
|
|
1294
|
+
if re.fullmatch(r"[0-9a-f]{40}", head):
|
|
1295
|
+
return head
|
|
1296
|
+
if not head.startswith("ref: "):
|
|
1297
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream HEAD is invalid")
|
|
1298
|
+
reference = head[5:]
|
|
1299
|
+
if ".." in Path(reference).parts or not reference.startswith("refs/"):
|
|
1300
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream HEAD reference is invalid")
|
|
1301
|
+
ref_path = git_dir / reference
|
|
1302
|
+
try:
|
|
1303
|
+
if ref_path.is_file():
|
|
1304
|
+
resolved = ref_path.read_text(encoding="utf-8").strip()
|
|
1305
|
+
if re.fullmatch(r"[0-9a-f]{40}", resolved):
|
|
1306
|
+
return resolved
|
|
1307
|
+
packed = (git_dir / "packed-refs").read_text(encoding="utf-8")
|
|
1308
|
+
except OSError as exc:
|
|
1309
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream HEAD reference is unavailable") from exc
|
|
1310
|
+
for line in packed.splitlines():
|
|
1311
|
+
if line.startswith(("#", "^")):
|
|
1312
|
+
continue
|
|
1313
|
+
parts = line.split(" ", 1)
|
|
1314
|
+
if len(parts) == 2 and parts[1] == reference and re.fullmatch(r"[0-9a-f]{40}", parts[0]):
|
|
1315
|
+
return parts[0]
|
|
1316
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream HEAD reference is unresolved")
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
def _verify_clean_checkout(root: Path) -> None:
|
|
1320
|
+
try:
|
|
1321
|
+
result = subprocess.run(
|
|
1322
|
+
["git", "-C", str(root), "status", "--porcelain", "--untracked-files=all"],
|
|
1323
|
+
check=False,
|
|
1324
|
+
capture_output=True,
|
|
1325
|
+
text=True,
|
|
1326
|
+
timeout=15,
|
|
1327
|
+
shell=False,
|
|
1328
|
+
)
|
|
1329
|
+
except (OSError, subprocess.SubprocessError) as exc:
|
|
1330
|
+
raise DriverError(
|
|
1331
|
+
"UPSTREAM_GIT_INVALID",
|
|
1332
|
+
"upstream cleanliness check could not run",
|
|
1333
|
+
) from exc
|
|
1334
|
+
if result.returncode != 0:
|
|
1335
|
+
raise DriverError("UPSTREAM_GIT_INVALID", "upstream cleanliness check failed")
|
|
1336
|
+
if result.stdout.strip():
|
|
1337
|
+
raise DriverError("UPSTREAM_CHECKOUT_DIRTY", "upstream checkout contains local changes")
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
def _select_profile(
|
|
1341
|
+
checkout: Checkout,
|
|
1342
|
+
mode: str,
|
|
1343
|
+
artifacts: Mapping[str, str],
|
|
1344
|
+
) -> PromptProfile:
|
|
1345
|
+
matches: list[tuple[str, Mapping[str, Any]]] = []
|
|
1346
|
+
for profile_id, raw_profile in checkout.manifest["prompt_profiles"].items():
|
|
1347
|
+
profile = _manifest_profile(raw_profile)
|
|
1348
|
+
if (
|
|
1349
|
+
profile["mode"] == mode
|
|
1350
|
+
and profile["prompt_artifact_hash"] == artifacts["prompt_artifact_hash"]
|
|
1351
|
+
and profile["guideline_artifact_hash"] == artifacts["guideline_artifact_hash"]
|
|
1352
|
+
):
|
|
1353
|
+
matches.append((profile_id, profile))
|
|
1354
|
+
if len(matches) != 1:
|
|
1355
|
+
raise DriverError(
|
|
1356
|
+
"PROMPT_ARTIFACT_MISMATCH",
|
|
1357
|
+
"request prompt and guideline hashes do not select one pinned ACON profile",
|
|
1358
|
+
)
|
|
1359
|
+
profile_id, raw = matches[0]
|
|
1360
|
+
prompt_dir = (checkout.root / raw["prompt_dir"]).resolve()
|
|
1361
|
+
try:
|
|
1362
|
+
prompt_dir.relative_to(checkout.root)
|
|
1363
|
+
except ValueError as exc: # pragma: no cover - manifest validation invariant
|
|
1364
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt directory escaped checkout") from exc
|
|
1365
|
+
if not prompt_dir.is_dir():
|
|
1366
|
+
raise DriverError("UPSTREAM_PROMPT_MISMATCH", "pinned prompt directory is unavailable")
|
|
1367
|
+
return PromptProfile(
|
|
1368
|
+
profile_id=profile_id,
|
|
1369
|
+
mode=mode,
|
|
1370
|
+
prompt_dir=prompt_dir,
|
|
1371
|
+
system_template=raw["system_template"],
|
|
1372
|
+
user_template=raw["user_template"],
|
|
1373
|
+
prompt_artifact_hash=raw["prompt_artifact_hash"],
|
|
1374
|
+
guideline_artifact_hash=raw["guideline_artifact_hash"],
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
def _manifest_profile(value: Any) -> Mapping[str, Any]:
|
|
1379
|
+
fields = {
|
|
1380
|
+
"mode",
|
|
1381
|
+
"prompt_dir",
|
|
1382
|
+
"system_template",
|
|
1383
|
+
"user_template",
|
|
1384
|
+
"prompt_files",
|
|
1385
|
+
"prompt_artifact_hash",
|
|
1386
|
+
"guideline_artifact_hash",
|
|
1387
|
+
}
|
|
1388
|
+
if not isinstance(value, Mapping) or set(value) != fields:
|
|
1389
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt profile is invalid")
|
|
1390
|
+
if value.get("mode") not in {"history", "observation"}:
|
|
1391
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt mode is invalid")
|
|
1392
|
+
prompt_dir = _manifest_relative_path(value.get("prompt_dir"), "prompt directory")
|
|
1393
|
+
system = _manifest_template_name(value.get("system_template"), "system template")
|
|
1394
|
+
user = _manifest_template_name(value.get("user_template"), "user template")
|
|
1395
|
+
files = value.get("prompt_files")
|
|
1396
|
+
if not isinstance(files, Mapping) or not files:
|
|
1397
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt_files is invalid")
|
|
1398
|
+
normalized_files: dict[str, str] = {}
|
|
1399
|
+
for relative, digest in files.items():
|
|
1400
|
+
relative, digest = _manifest_path_hash(relative, digest, "prompt file")
|
|
1401
|
+
if not relative.startswith(prompt_dir + "/"):
|
|
1402
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt file escaped prompt directory")
|
|
1403
|
+
normalized_files[relative] = digest
|
|
1404
|
+
required = {f"{prompt_dir}/{system}.jinja", f"{prompt_dir}/{user}.jinja"}
|
|
1405
|
+
if not required <= set(normalized_files):
|
|
1406
|
+
raise DriverError("MANIFEST_INVALID", "manifest prompt profile lacks selected templates")
|
|
1407
|
+
return {
|
|
1408
|
+
"mode": value["mode"],
|
|
1409
|
+
"prompt_dir": prompt_dir,
|
|
1410
|
+
"system_template": system,
|
|
1411
|
+
"user_template": user,
|
|
1412
|
+
"prompt_files": normalized_files,
|
|
1413
|
+
"prompt_artifact_hash": _sha_text(
|
|
1414
|
+
value.get("prompt_artifact_hash"),
|
|
1415
|
+
"manifest prompt_artifact_hash",
|
|
1416
|
+
manifest=True,
|
|
1417
|
+
),
|
|
1418
|
+
"guideline_artifact_hash": _sha_text(
|
|
1419
|
+
value.get("guideline_artifact_hash"),
|
|
1420
|
+
"manifest guideline_artifact_hash",
|
|
1421
|
+
manifest=True,
|
|
1422
|
+
),
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
|
|
1426
|
+
def _parse_artifacts(value: Any) -> Mapping[str, str]:
|
|
1427
|
+
if not isinstance(value, Mapping) or set(value) != _ARTIFACT_FIELDS:
|
|
1428
|
+
raise DriverError("INVALID_REQUEST", "ACON artifacts do not match schema")
|
|
1429
|
+
result: dict[str, str] = {}
|
|
1430
|
+
for field in sorted(_ARTIFACT_FIELDS):
|
|
1431
|
+
item = _bounded_text(value.get(field), field, allow_empty=False, limit=1024)
|
|
1432
|
+
if field.endswith("_hash") and not _HEX_SHA256.fullmatch(item):
|
|
1433
|
+
raise DriverError("INVALID_REQUEST", f"{field} must be a SHA-256 digest")
|
|
1434
|
+
result[field] = item
|
|
1435
|
+
return result
|
|
1436
|
+
|
|
1437
|
+
|
|
1438
|
+
def _validate_bound_revisions(
|
|
1439
|
+
environment: Mapping[str, str],
|
|
1440
|
+
artifacts: Mapping[str, str],
|
|
1441
|
+
) -> None:
|
|
1442
|
+
bindings = {
|
|
1443
|
+
OPTIMIZER_REVISION_ENV: "optimizer_model_revision",
|
|
1444
|
+
TARGET_REVISION_ENV: "target_model_revision",
|
|
1445
|
+
TOKENIZER_REVISION_ENV: "tokenizer_revision",
|
|
1446
|
+
}
|
|
1447
|
+
for environment_key, artifact_key in bindings.items():
|
|
1448
|
+
configured = environment.get(environment_key, "").strip()
|
|
1449
|
+
if not configured:
|
|
1450
|
+
raise DriverError(
|
|
1451
|
+
"ARTIFACT_REVISION_MISSING",
|
|
1452
|
+
f"{environment_key} is required for runtime attestation",
|
|
1453
|
+
)
|
|
1454
|
+
if configured != artifacts[artifact_key]:
|
|
1455
|
+
raise DriverError(
|
|
1456
|
+
"ARTIFACT_REVISION_MISMATCH",
|
|
1457
|
+
f"{environment_key} disagrees with the request artifact identity",
|
|
1458
|
+
)
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
def _history_controls(value: Any) -> Mapping[str, Any]:
|
|
1462
|
+
fields = {
|
|
1463
|
+
"summarization_threshold",
|
|
1464
|
+
"preserve_last_k_turns",
|
|
1465
|
+
"summary_rule",
|
|
1466
|
+
"summary_interval",
|
|
1467
|
+
"history_version",
|
|
1468
|
+
}
|
|
1469
|
+
if not isinstance(value, Mapping) or set(value) != fields:
|
|
1470
|
+
raise DriverError("INVALID_REQUEST", "ACON history controls do not match schema")
|
|
1471
|
+
threshold = _threshold(value.get("summarization_threshold"))
|
|
1472
|
+
preserve = _nonnegative_int(value.get("preserve_last_k_turns"), "preserve_last_k_turns")
|
|
1473
|
+
rule = value.get("summary_rule")
|
|
1474
|
+
if rule not in {"accumulate", "reset"}:
|
|
1475
|
+
raise DriverError("INVALID_REQUEST", "summary_rule must be accumulate or reset")
|
|
1476
|
+
interval = value.get("summary_interval")
|
|
1477
|
+
if (
|
|
1478
|
+
isinstance(interval, bool)
|
|
1479
|
+
or not isinstance(interval, int)
|
|
1480
|
+
or (interval != -1 and interval < 1)
|
|
1481
|
+
):
|
|
1482
|
+
raise DriverError("INVALID_REQUEST", "summary_interval must be -1 or positive")
|
|
1483
|
+
if value.get("history_version") != 1 or isinstance(value.get("history_version"), bool):
|
|
1484
|
+
raise DriverError("INVALID_REQUEST", "pinned ACON supports history_version 1 only")
|
|
1485
|
+
return {
|
|
1486
|
+
"summarization_threshold": threshold,
|
|
1487
|
+
"preserve_last_k_turns": preserve,
|
|
1488
|
+
"summary_rule": rule,
|
|
1489
|
+
"summary_interval": interval,
|
|
1490
|
+
"history_version": 1,
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
def _observation_controls(value: Any) -> Mapping[str, Any]:
|
|
1495
|
+
fields = {
|
|
1496
|
+
"summarization_threshold",
|
|
1497
|
+
"context_index",
|
|
1498
|
+
"add_previous_history_summary",
|
|
1499
|
+
"observation_version",
|
|
1500
|
+
}
|
|
1501
|
+
if not isinstance(value, Mapping) or set(value) != fields:
|
|
1502
|
+
raise DriverError("INVALID_REQUEST", "ACON observation controls do not match schema")
|
|
1503
|
+
threshold = _threshold(value.get("summarization_threshold"))
|
|
1504
|
+
index = value.get("context_index")
|
|
1505
|
+
if isinstance(index, bool) or not isinstance(index, int):
|
|
1506
|
+
raise DriverError("INVALID_REQUEST", "context_index must be an integer")
|
|
1507
|
+
add_previous = value.get("add_previous_history_summary")
|
|
1508
|
+
if not isinstance(add_previous, bool):
|
|
1509
|
+
raise DriverError("INVALID_REQUEST", "add_previous_history_summary must be boolean")
|
|
1510
|
+
version = value.get("observation_version")
|
|
1511
|
+
if version != 1 or isinstance(version, bool):
|
|
1512
|
+
raise DriverError("INVALID_REQUEST", "pinned ACON supports observation_version 1 only")
|
|
1513
|
+
return {
|
|
1514
|
+
"summarization_threshold": threshold,
|
|
1515
|
+
"context_index": index,
|
|
1516
|
+
"add_previous_history_summary": add_previous,
|
|
1517
|
+
"observation_version": 1,
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
|
|
1521
|
+
def _messages(value: Any) -> list[Mapping[str, Any]]:
|
|
1522
|
+
return _message_sequence(value, "raw_messages")
|
|
1523
|
+
|
|
1524
|
+
|
|
1525
|
+
def _message_sequence(value: Any, label: str) -> list[Mapping[str, Any]]:
|
|
1526
|
+
if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
|
|
1527
|
+
raise DriverError("INVALID_REQUEST", f"{label} must be a sequence")
|
|
1528
|
+
result: list[Mapping[str, Any]] = []
|
|
1529
|
+
for index, item in enumerate(value):
|
|
1530
|
+
if not isinstance(item, Mapping):
|
|
1531
|
+
raise DriverError("INVALID_REQUEST", f"{label}[{index}] must be an object")
|
|
1532
|
+
role = item.get("role")
|
|
1533
|
+
content = item.get("content")
|
|
1534
|
+
if not isinstance(role, str) or not role or not isinstance(content, str):
|
|
1535
|
+
raise DriverError("INVALID_REQUEST", f"{label}[{index}] role/content is invalid")
|
|
1536
|
+
try:
|
|
1537
|
+
result.append(json.loads(_canonical_json(item)))
|
|
1538
|
+
except (TypeError, ValueError) as exc:
|
|
1539
|
+
raise DriverError("INVALID_REQUEST", f"{label}[{index}] is not canonical JSON") from exc
|
|
1540
|
+
return result
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
def _history_text(messages: Sequence[Mapping[str, Any]]) -> str:
|
|
1544
|
+
parts: list[str] = []
|
|
1545
|
+
for index, message in enumerate(messages):
|
|
1546
|
+
if index == 1 and message["role"] == "user":
|
|
1547
|
+
continue
|
|
1548
|
+
if message["role"] == "user":
|
|
1549
|
+
parts.append(f"USER:\n{message['content']}\n\n")
|
|
1550
|
+
elif message["role"] == "assistant":
|
|
1551
|
+
parts.append(f"ASSISTANT:\n{message['content']}\n\n")
|
|
1552
|
+
return "".join(parts).strip()
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
def _preserved_turn_indices(
|
|
1556
|
+
messages: Sequence[Mapping[str, Any]],
|
|
1557
|
+
preserve_last_k_turns: int,
|
|
1558
|
+
) -> tuple[int, ...]:
|
|
1559
|
+
if not messages or preserve_last_k_turns <= 0:
|
|
1560
|
+
return ()
|
|
1561
|
+
turns: list[int] = []
|
|
1562
|
+
index = len(messages) - 1
|
|
1563
|
+
count = 0
|
|
1564
|
+
while index >= 0 and count < preserve_last_k_turns:
|
|
1565
|
+
if messages[index]["role"] != "user":
|
|
1566
|
+
index -= 1
|
|
1567
|
+
continue
|
|
1568
|
+
user_index = index
|
|
1569
|
+
assistant_index = index - 1
|
|
1570
|
+
while assistant_index >= 0 and messages[assistant_index]["role"] != "assistant":
|
|
1571
|
+
assistant_index -= 1
|
|
1572
|
+
if assistant_index >= 0:
|
|
1573
|
+
turns[0:0] = (assistant_index, user_index)
|
|
1574
|
+
count += 1
|
|
1575
|
+
index = assistant_index - 1
|
|
1576
|
+
else:
|
|
1577
|
+
turns.insert(0, user_index)
|
|
1578
|
+
count += 1
|
|
1579
|
+
index -= 1
|
|
1580
|
+
first_user = messages[1]["content"] if len(messages) > 1 else ""
|
|
1581
|
+
return tuple(
|
|
1582
|
+
item
|
|
1583
|
+
for item in turns
|
|
1584
|
+
if messages[item]["role"] != "system"
|
|
1585
|
+
and not (messages[item]["role"] == "user" and messages[item]["content"] == first_user)
|
|
1586
|
+
)
|
|
1587
|
+
|
|
1588
|
+
|
|
1589
|
+
def _history_material(
|
|
1590
|
+
messages: Sequence[Mapping[str, Any]],
|
|
1591
|
+
preserved: Sequence[Mapping[str, Any]],
|
|
1592
|
+
) -> str:
|
|
1593
|
+
start = len(messages)
|
|
1594
|
+
if preserved:
|
|
1595
|
+
first = preserved[0]
|
|
1596
|
+
for index, message in enumerate(messages):
|
|
1597
|
+
if message["role"] == first["role"] and message["content"] == first["content"]:
|
|
1598
|
+
start = index
|
|
1599
|
+
break
|
|
1600
|
+
summarization = messages[:start] if start > 2 else ()
|
|
1601
|
+
return _history_text(summarization)
|
|
1602
|
+
|
|
1603
|
+
|
|
1604
|
+
def _integer_sequence(value: Any, label: str) -> list[int]:
|
|
1605
|
+
if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
|
|
1606
|
+
raise DriverError("INVALID_REQUEST", f"{label} must be an integer sequence")
|
|
1607
|
+
result = list(value)
|
|
1608
|
+
if any(isinstance(item, bool) or not isinstance(item, int) or item < 0 for item in result):
|
|
1609
|
+
raise DriverError("INVALID_REQUEST", f"{label} must contain non-negative integers")
|
|
1610
|
+
return result
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
def _threshold(value: Any) -> int:
|
|
1614
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < -1:
|
|
1615
|
+
raise DriverError("INVALID_REQUEST", "summarization_threshold must be -1 or non-negative")
|
|
1616
|
+
return value
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
def _seed(value: Any) -> int:
|
|
1620
|
+
return _nonnegative_int(value, "seed")
|
|
1621
|
+
|
|
1622
|
+
|
|
1623
|
+
def _nonnegative_int(value: Any, label: str) -> int:
|
|
1624
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
1625
|
+
raise DriverError("INVALID_REQUEST", f"{label} must be a non-negative integer")
|
|
1626
|
+
return value
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
def _upstream_text(value: Any, label: str) -> str:
|
|
1630
|
+
if not isinstance(value, str) or len(value) > MAX_TEXT_CHARS:
|
|
1631
|
+
raise DriverError("UPSTREAM_OUTPUT_INVALID", f"upstream {label} is invalid")
|
|
1632
|
+
return value
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
def _bounded_text(
|
|
1636
|
+
value: Any,
|
|
1637
|
+
label: str,
|
|
1638
|
+
*,
|
|
1639
|
+
allow_empty: bool = True,
|
|
1640
|
+
limit: int = MAX_TEXT_CHARS,
|
|
1641
|
+
) -> str:
|
|
1642
|
+
if not isinstance(value, str) or len(value) > limit or (not allow_empty and not value.strip()):
|
|
1643
|
+
qualifier = "bounded text" if allow_empty else "bounded non-empty text"
|
|
1644
|
+
raise DriverError("INVALID_REQUEST", f"{label} must be {qualifier}")
|
|
1645
|
+
return value
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
def _sha_text(value: Any, label: str, *, manifest: bool = False) -> str:
|
|
1649
|
+
code = "MANIFEST_INVALID" if manifest else "INVALID_REQUEST"
|
|
1650
|
+
if not isinstance(value, str) or not _HEX_SHA256.fullmatch(value):
|
|
1651
|
+
raise DriverError(code, f"{label} must be a SHA-256 digest")
|
|
1652
|
+
return value
|
|
1653
|
+
|
|
1654
|
+
|
|
1655
|
+
def _manifest_relative_path(value: Any, label: str) -> str:
|
|
1656
|
+
if not isinstance(value, str) or not value or Path(value).is_absolute():
|
|
1657
|
+
raise DriverError("MANIFEST_INVALID", f"manifest {label} is invalid")
|
|
1658
|
+
path = Path(value)
|
|
1659
|
+
if ".." in path.parts or "." in path.parts:
|
|
1660
|
+
raise DriverError("MANIFEST_INVALID", f"manifest {label} is invalid")
|
|
1661
|
+
return path.as_posix()
|
|
1662
|
+
|
|
1663
|
+
|
|
1664
|
+
def _manifest_template_name(value: Any, label: str) -> str:
|
|
1665
|
+
if not isinstance(value, str) or not re.fullmatch(r"[A-Za-z0-9_-]+", value):
|
|
1666
|
+
raise DriverError("MANIFEST_INVALID", f"manifest {label} is invalid")
|
|
1667
|
+
return value
|
|
1668
|
+
|
|
1669
|
+
|
|
1670
|
+
def _manifest_path_hash(value: Any, digest: Any, label: str) -> tuple[str, str]:
|
|
1671
|
+
relative = _manifest_relative_path(value, label)
|
|
1672
|
+
return relative, _sha_text(digest, f"manifest {label} hash", manifest=True)
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
def _safe_file(root: Path, relative: str) -> Path:
|
|
1676
|
+
path = (root / relative).resolve()
|
|
1677
|
+
try:
|
|
1678
|
+
path.relative_to(root)
|
|
1679
|
+
except ValueError as exc:
|
|
1680
|
+
raise DriverError("UPSTREAM_PATH_INVALID", "upstream artifact escaped checkout") from exc
|
|
1681
|
+
if not path.is_file():
|
|
1682
|
+
raise DriverError("UPSTREAM_ARTIFACT_MISSING", "pinned upstream artifact is unavailable")
|
|
1683
|
+
return path
|
|
1684
|
+
|
|
1685
|
+
|
|
1686
|
+
def _sha256_file(path: Path, label: str) -> str:
|
|
1687
|
+
try:
|
|
1688
|
+
return _sha256_bytes(path.read_bytes())
|
|
1689
|
+
except OSError as exc:
|
|
1690
|
+
raise DriverError(
|
|
1691
|
+
"UPSTREAM_ARTIFACT_MISSING",
|
|
1692
|
+
f"pinned upstream {label} is unavailable",
|
|
1693
|
+
) from exc
|
|
1694
|
+
|
|
1695
|
+
|
|
1696
|
+
def _environment_float(
|
|
1697
|
+
environment: Mapping[str, str],
|
|
1698
|
+
key: str,
|
|
1699
|
+
*,
|
|
1700
|
+
default: float,
|
|
1701
|
+
minimum: float,
|
|
1702
|
+
maximum: float,
|
|
1703
|
+
) -> float:
|
|
1704
|
+
raw = environment.get(key, "").strip()
|
|
1705
|
+
if not raw:
|
|
1706
|
+
return default
|
|
1707
|
+
try:
|
|
1708
|
+
value = float(raw)
|
|
1709
|
+
except ValueError as exc:
|
|
1710
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{key} must be numeric") from exc
|
|
1711
|
+
if not minimum <= value <= maximum:
|
|
1712
|
+
raise DriverError(
|
|
1713
|
+
"MODEL_CONFIGURATION_INVALID",
|
|
1714
|
+
f"{key} must be between {minimum} and {maximum}",
|
|
1715
|
+
)
|
|
1716
|
+
return value
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
def _environment_optional_positive_int(
|
|
1720
|
+
environment: Mapping[str, str],
|
|
1721
|
+
key: str,
|
|
1722
|
+
) -> int | None:
|
|
1723
|
+
raw = environment.get(key, "").strip()
|
|
1724
|
+
if not raw:
|
|
1725
|
+
return None
|
|
1726
|
+
try:
|
|
1727
|
+
value = int(raw)
|
|
1728
|
+
except ValueError as exc:
|
|
1729
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{key} must be an integer") from exc
|
|
1730
|
+
if value < 1:
|
|
1731
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{key} must be positive")
|
|
1732
|
+
return value
|
|
1733
|
+
|
|
1734
|
+
|
|
1735
|
+
def _environment_json_mapping(
|
|
1736
|
+
environment: Mapping[str, str],
|
|
1737
|
+
key: str,
|
|
1738
|
+
) -> Mapping[str, Any]:
|
|
1739
|
+
raw = environment.get(key, "").strip()
|
|
1740
|
+
if not raw:
|
|
1741
|
+
return {}
|
|
1742
|
+
try:
|
|
1743
|
+
value = json.loads(raw)
|
|
1744
|
+
except json.JSONDecodeError as exc:
|
|
1745
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{key} must be valid JSON") from exc
|
|
1746
|
+
if not isinstance(value, Mapping):
|
|
1747
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{key} must contain a JSON object")
|
|
1748
|
+
return value
|
|
1749
|
+
|
|
1750
|
+
|
|
1751
|
+
def _chat_completions_endpoint(base_url: str) -> str:
|
|
1752
|
+
try:
|
|
1753
|
+
parsed = urllib.parse.urlsplit(base_url)
|
|
1754
|
+
except ValueError as exc:
|
|
1755
|
+
raise DriverError("MODEL_CONFIGURATION_INVALID", f"{BASE_URL_ENV} is invalid") from exc
|
|
1756
|
+
if (
|
|
1757
|
+
parsed.scheme not in {"http", "https"}
|
|
1758
|
+
or not parsed.netloc
|
|
1759
|
+
or parsed.username is not None
|
|
1760
|
+
or parsed.password is not None
|
|
1761
|
+
or parsed.query
|
|
1762
|
+
or parsed.fragment
|
|
1763
|
+
):
|
|
1764
|
+
raise DriverError(
|
|
1765
|
+
"MODEL_CONFIGURATION_INVALID",
|
|
1766
|
+
f"{BASE_URL_ENV} must be an HTTP(S) URL without credentials or query",
|
|
1767
|
+
)
|
|
1768
|
+
path = parsed.path.rstrip("/")
|
|
1769
|
+
if not path.endswith("/chat/completions"):
|
|
1770
|
+
path = f"{path}/chat/completions"
|
|
1771
|
+
return urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, path, "", ""))
|
|
1772
|
+
|
|
1773
|
+
|
|
1774
|
+
def _dependency_error(error: ModuleNotFoundError) -> DriverError:
|
|
1775
|
+
name = error.name if isinstance(error.name, str) else "optional dependency"
|
|
1776
|
+
safe_name = re.sub(r"[^A-Za-z0-9_.-]", "?", name)[:200]
|
|
1777
|
+
return DriverError(
|
|
1778
|
+
"UPSTREAM_DEPENDENCY_MISSING",
|
|
1779
|
+
f"required pinned ACON dependency is missing: {safe_name}",
|
|
1780
|
+
)
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
def _canonical_json(value: Any) -> str:
|
|
1784
|
+
return json.dumps(
|
|
1785
|
+
value,
|
|
1786
|
+
ensure_ascii=False,
|
|
1787
|
+
sort_keys=True,
|
|
1788
|
+
separators=(",", ":"),
|
|
1789
|
+
allow_nan=False,
|
|
1790
|
+
)
|
|
1791
|
+
|
|
1792
|
+
|
|
1793
|
+
def _content_hash(value: Any) -> str:
|
|
1794
|
+
return _sha256_bytes(_canonical_json(value).encode("utf-8"))
|
|
1795
|
+
|
|
1796
|
+
|
|
1797
|
+
def _sha256_bytes(value: bytes) -> str:
|
|
1798
|
+
return hashlib.sha256(value).hexdigest()
|
|
1799
|
+
|
|
1800
|
+
|
|
1801
|
+
def _error_response(error: DriverError, request: Any = None) -> Mapping[str, Any]:
|
|
1802
|
+
response: dict[str, Any] = {
|
|
1803
|
+
"status": "error",
|
|
1804
|
+
"failure_code": error.code,
|
|
1805
|
+
"error_message": error.public_message,
|
|
1806
|
+
}
|
|
1807
|
+
if error.usage is None:
|
|
1808
|
+
response["consumed"] = False
|
|
1809
|
+
else:
|
|
1810
|
+
calls, input_tokens, output_tokens = error.usage
|
|
1811
|
+
response["consumed"] = bool(error.consumed or calls)
|
|
1812
|
+
response["usage"] = {
|
|
1813
|
+
"model_calls": calls,
|
|
1814
|
+
"retrieval_calls": 0,
|
|
1815
|
+
"input_tokens": input_tokens,
|
|
1816
|
+
"output_tokens": output_tokens,
|
|
1817
|
+
}
|
|
1818
|
+
response["usage_is_lower_bound"] = True
|
|
1819
|
+
if isinstance(request, Mapping):
|
|
1820
|
+
for field in ("request_id", "upstream_commit", "protocol_version"):
|
|
1821
|
+
value = request.get(field)
|
|
1822
|
+
if isinstance(value, str) and len(value) <= 256:
|
|
1823
|
+
response[field] = value
|
|
1824
|
+
return response
|
|
1825
|
+
|
|
1826
|
+
|
|
1827
|
+
def serve_jsonl(driver: ACONDriver, source: TextIO, sink: TextIO) -> int:
|
|
1828
|
+
for line in source:
|
|
1829
|
+
request: Any = None
|
|
1830
|
+
if len(line.encode("utf-8")) > MAX_REQUEST_BYTES:
|
|
1831
|
+
response: Mapping[str, Any] = _error_response(
|
|
1832
|
+
DriverError("REQUEST_TOO_LARGE", "worker request exceeds its byte limit")
|
|
1833
|
+
)
|
|
1834
|
+
else:
|
|
1835
|
+
try:
|
|
1836
|
+
request = json.loads(line)
|
|
1837
|
+
response = driver.run(request)
|
|
1838
|
+
except json.JSONDecodeError:
|
|
1839
|
+
response = _error_response(
|
|
1840
|
+
DriverError("INVALID_JSON", "worker request is not valid JSON")
|
|
1841
|
+
)
|
|
1842
|
+
except DriverError as exc:
|
|
1843
|
+
response = _error_response(exc, request)
|
|
1844
|
+
except Exception:
|
|
1845
|
+
response = _error_response(
|
|
1846
|
+
DriverError("WORKER_FAILURE", "worker failed without a safe diagnostic"),
|
|
1847
|
+
request,
|
|
1848
|
+
)
|
|
1849
|
+
sink.write(_canonical_json(response) + "\n")
|
|
1850
|
+
sink.flush()
|
|
1851
|
+
return 0
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
def main() -> int:
|
|
1855
|
+
return serve_jsonl(ACONDriver(), sys.stdin, sys.stdout)
|
|
1856
|
+
|
|
1857
|
+
|
|
1858
|
+
if __name__ == "__main__":
|
|
1859
|
+
raise SystemExit(main())
|