agentevolve-optimizer 0.5.0__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.
Files changed (414) hide show
  1. agent_evolve/__init__.py +722 -0
  2. agent_evolve/agentic.py +2800 -0
  3. agent_evolve/api.py +767 -0
  4. agent_evolve/application/__init__.py +1580 -0
  5. agent_evolve/application/action_allocation.py +744 -0
  6. agent_evolve/application/action_allocation_frame.py +347 -0
  7. agent_evolve/application/action_allocation_frame_commit.py +185 -0
  8. agent_evolve/application/action_allocation_frame_commit_v3.py +184 -0
  9. agent_evolve/application/action_allocation_frame_v3.py +338 -0
  10. agent_evolve/application/action_archive_value.py +497 -0
  11. agent_evolve/application/action_evidence_consistency.py +455 -0
  12. agent_evolve/application/action_forecast_partitioning.py +1471 -0
  13. agent_evolve/application/action_metric_projection.py +211 -0
  14. agent_evolve/application/action_role_value.py +680 -0
  15. agent_evolve/application/action_score_authorities.py +363 -0
  16. agent_evolve/application/action_structural_signature.py +116 -0
  17. agent_evolve/application/action_target_realization.py +402 -0
  18. agent_evolve/application/agentic_evolution.py +7734 -0
  19. agent_evolve/application/agentic_portfolio_residual_expert.py +835 -0
  20. agent_evolve/application/anchor_residual_identification.py +463 -0
  21. agent_evolve/application/archive_conditioned_action_target.py +208 -0
  22. agent_evolve/application/artifact_journal.py +246 -0
  23. agent_evolve/application/artifact_replay.py +347 -0
  24. agent_evolve/application/budgeted_optimizer.py +1828 -0
  25. agent_evolve/application/calibrated_campaign.py +485 -0
  26. agent_evolve/application/calibrated_current_prefix_forecast_opportunity.py +322 -0
  27. agent_evolve/application/calibrated_positive_gain_opportunity.py +1581 -0
  28. agent_evolve/application/campaign_capacity_recourse.py +254 -0
  29. agent_evolve/application/campaign_contextual_outcomes.py +119 -0
  30. agent_evolve/application/campaign_diagnostic_blocks.py +930 -0
  31. agent_evolve/application/campaign_evidence_registry.py +262 -0
  32. agent_evolve/application/campaign_execution.py +2537 -0
  33. agent_evolve/application/campaign_generation_audit.py +942 -0
  34. agent_evolve/application/campaign_learning.py +1812 -0
  35. agent_evolve/application/campaign_learning_runtime.py +1977 -0
  36. agent_evolve/application/campaign_search_phase.py +227 -0
  37. agent_evolve/application/campaign_selector_context_extension.py +220 -0
  38. agent_evolve/application/campaign_variation_envelope.py +649 -0
  39. agent_evolve/application/campaign_variation_trace.py +451 -0
  40. agent_evolve/application/candidate_archive_consequence.py +128 -0
  41. agent_evolve/application/causal_opportunity_portfolio_gate.py +385 -0
  42. agent_evolve/application/composite_outcome_updater.py +145 -0
  43. agent_evolve/application/composition_portfolio_selection.py +363 -0
  44. agent_evolve/application/concurrent_stage.py +144 -0
  45. agent_evolve/application/contextual_action_allocation.py +181 -0
  46. agent_evolve/application/contextual_campaign_outcomes.py +267 -0
  47. agent_evolve/application/contextual_campaign_planning.py +1366 -0
  48. agent_evolve/application/contextual_delayed_credit.py +651 -0
  49. agent_evolve/application/contextual_search_controller.py +2374 -0
  50. agent_evolve/application/current_prefix_forecast_opportunity.py +714 -0
  51. agent_evolve/application/decision_metric_projection.py +112 -0
  52. agent_evolve/application/derived_action_semantics.py +129 -0
  53. agent_evolve/application/detailed_evaluation.py +449 -0
  54. agent_evolve/application/earned_lineage.py +1011 -0
  55. agent_evolve/application/effective_choice_audit.py +484 -0
  56. agent_evolve/application/empirical_consequence_calibration.py +908 -0
  57. agent_evolve/application/evaluation_accounting.py +325 -0
  58. agent_evolve/application/evaluation_cache.py +199 -0
  59. agent_evolve/application/evaluation_escrow.py +547 -0
  60. agent_evolve/application/evaluation_recourse.py +253 -0
  61. agent_evolve/application/event_recorder.py +151 -0
  62. agent_evolve/application/evolution_campaign.py +1840 -0
  63. agent_evolve/application/executable_hypothesis.py +323 -0
  64. agent_evolve/application/factorial_branch_pilot.py +772 -0
  65. agent_evolve/application/finite_acquisition_capacity_recourse.py +672 -0
  66. agent_evolve/application/finite_acquisition_residual_expert.py +373 -0
  67. agent_evolve/application/finite_acquisition_variation_envelope.py +802 -0
  68. agent_evolve/application/finite_action_hypothesis_semantics.py +446 -0
  69. agent_evolve/application/finite_action_selection.py +188 -0
  70. agent_evolve/application/finite_action_set.py +306 -0
  71. agent_evolve/application/finite_action_transition.py +537 -0
  72. agent_evolve/application/finite_variation_eligibility.py +296 -0
  73. agent_evolve/application/forecast_geometry_portfolio.py +799 -0
  74. agent_evolve/application/forecast_opportunity_shadow_calibration.py +316 -0
  75. agent_evolve/application/front_proximity_admission.py +311 -0
  76. agent_evolve/application/front_proximity_parent_basis.py +458 -0
  77. agent_evolve/application/frozen_hurdle_score.py +659 -0
  78. agent_evolve/application/g3_causal_screen.py +2257 -0
  79. agent_evolve/application/g3_causal_validation.py +1046 -0
  80. agent_evolve/application/g3_postseal_curation.py +818 -0
  81. agent_evolve/application/gated_agentic_generator.py +205 -0
  82. agent_evolve/application/generation_feedback.py +293 -0
  83. agent_evolve/application/generative_proposal_journal.py +185 -0
  84. agent_evolve/application/geometry_conditional_elasticity.py +453 -0
  85. agent_evolve/application/global_wave_action_allocation.py +1151 -0
  86. agent_evolve/application/head_mass_conditional_seat.py +268 -0
  87. agent_evolve/application/identifiable_reflection_evidence.py +1147 -0
  88. agent_evolve/application/identifiable_reflection_learning.py +395 -0
  89. agent_evolve/application/identifiable_reflection_request.py +364 -0
  90. agent_evolve/application/in_memory_residual_archive.py +341 -0
  91. agent_evolve/application/insight_memory.py +1804 -0
  92. agent_evolve/application/live_runtime_manifest.py +758 -0
  93. agent_evolve/application/llm_task_queue.py +769 -0
  94. agent_evolve/application/matched_finite_action_block.py +409 -0
  95. agent_evolve/application/materialized_action_broker.py +2328 -0
  96. agent_evolve/application/materialized_action_constraints.py +83 -0
  97. agent_evolve/application/materialized_variation.py +211 -0
  98. agent_evolve/application/multi_option_evolution.py +1536 -0
  99. agent_evolve/application/outcome_adaptive_action_racing.py +2827 -0
  100. agent_evolve/application/outcome_adaptive_residual_campaign_runtime.py +580 -0
  101. agent_evolve/application/outcome_adaptive_residual_portfolio_evolution.py +3671 -0
  102. agent_evolve/application/outcome_conditioned_portfolio_selection.py +1374 -0
  103. agent_evolve/application/outcome_relation.py +193 -0
  104. agent_evolve/application/paired_allocation_comparison.py +241 -0
  105. agent_evolve/application/paired_block_schedule.py +127 -0
  106. agent_evolve/application/parent_measurement.py +226 -0
  107. agent_evolve/application/pareto_archive.py +811 -0
  108. agent_evolve/application/portfolio_campaign_runtime.py +4739 -0
  109. agent_evolve/application/portfolio_evolution.py +2950 -0
  110. agent_evolve/application/portfolio_hypothesis_observations.py +814 -0
  111. agent_evolve/application/portfolio_memory_attribution.py +581 -0
  112. agent_evolve/application/portfolio_memory_dose.py +788 -0
  113. agent_evolve/application/portfolio_memory_matched_control.py +938 -0
  114. agent_evolve/application/portfolio_memory_transfer.py +297 -0
  115. agent_evolve/application/portfolio_optimization_memory.py +363 -0
  116. agent_evolve/application/portfolio_outcome_feedback.py +1613 -0
  117. agent_evolve/application/portfolio_projection.py +335 -0
  118. agent_evolve/application/portfolio_recombination.py +2032 -0
  119. agent_evolve/application/post_evolution_reflection.py +834 -0
  120. agent_evolve/application/postcommit_rank_authority.py +245 -0
  121. agent_evolve/application/precommitted_portfolio_racing.py +2762 -0
  122. agent_evolve/application/prequential_archive_opportunity_calibration.py +1154 -0
  123. agent_evolve/application/prequential_residual_exploration.py +343 -0
  124. agent_evolve/application/prequential_score_portfolio.py +954 -0
  125. agent_evolve/application/projections.py +292 -0
  126. agent_evolve/application/protected_action_committee.py +1027 -0
  127. agent_evolve/application/protected_branch_pilot.py +376 -0
  128. agent_evolve/application/protected_current_prefix_forecast_opportunity.py +552 -0
  129. agent_evolve/application/provider_replay.py +910 -0
  130. agent_evolve/application/rank_balanced_causal_pilot.py +1372 -0
  131. agent_evolve/application/recombination_residual_expert.py +403 -0
  132. agent_evolve/application/reflection_workflow.py +571 -0
  133. agent_evolve/application/region_conditional_credit.py +911 -0
  134. agent_evolve/application/residual_campaign_runtime.py +531 -0
  135. agent_evolve/application/residual_headroom_campaign_runtime.py +459 -0
  136. agent_evolve/application/residual_headroom_ledger.py +1544 -0
  137. agent_evolve/application/residual_learning_transaction.py +396 -0
  138. agent_evolve/application/residual_portfolio_evolution.py +1228 -0
  139. agent_evolve/application/residual_reachability.py +749 -0
  140. agent_evolve/application/residual_stage_credit.py +499 -0
  141. agent_evolve/application/same_prefix_paired_audit.py +1580 -0
  142. agent_evolve/application/semantic_coverage_score_portfolio.py +838 -0
  143. agent_evolve/application/sequential_lineage_allocation.py +1017 -0
  144. agent_evolve/application/sequential_market_replay.py +1395 -0
  145. agent_evolve/application/sequential_residual_campaign_runtime.py +305 -0
  146. agent_evolve/application/sequential_residual_portfolio_evolution.py +940 -0
  147. agent_evolve/application/single_score_action_allocation.py +299 -0
  148. agent_evolve/application/source_exposure_allocation.py +906 -0
  149. agent_evolve/application/staged_memory.py +210 -0
  150. agent_evolve/application/stratified_cold_start_allocation.py +732 -0
  151. agent_evolve/application/support_guarded_hurdle_score.py +549 -0
  152. agent_evolve/application/target_conditioned_action_forecast.py +595 -0
  153. agent_evolve/application/target_conditioned_campaign.py +566 -0
  154. agent_evolve/application/treatment_assignment.py +201 -0
  155. agent_evolve/application/trusted_objective_evidence.py +217 -0
  156. agent_evolve/application/two_stage_action_evolution.py +1131 -0
  157. agent_evolve/application/v8lite_allocation_policy.py +1083 -0
  158. agent_evolve/application/v9_candidate_policy.py +1303 -0
  159. agent_evolve/bootstrap.py +108 -0
  160. agent_evolve/campaign_presets.py +517 -0
  161. agent_evolve/campaign_profiles.py +452 -0
  162. agent_evolve/campaign_variation_topology.py +288 -0
  163. agent_evolve/campaign_workload.py +950 -0
  164. agent_evolve/cli.py +797 -0
  165. agent_evolve/contract.py +241 -0
  166. agent_evolve/core/__init__.py +91 -0
  167. agent_evolve/core/action_semantics.py +411 -0
  168. agent_evolve/core/authored.py +105 -0
  169. agent_evolve/core/formatting.py +286 -0
  170. agent_evolve/core/optimization_semantics.py +324 -0
  171. agent_evolve/core/problem.py +167 -0
  172. agent_evolve/core/results.py +323 -0
  173. agent_evolve/core/stats.py +70 -0
  174. agent_evolve/core/telemetry.py +100 -0
  175. agent_evolve/domain/__init__.py +89 -0
  176. agent_evolve/domain/artifact.py +162 -0
  177. agent_evolve/domain/durable_text.py +68 -0
  178. agent_evolve/domain/event.py +1454 -0
  179. agent_evolve/domain/finite_action_set.py +426 -0
  180. agent_evolve/domain/finite_variation.py +526 -0
  181. agent_evolve/domain/generative_emission.py +559 -0
  182. agent_evolve/domain/ids.py +163 -0
  183. agent_evolve/domain/inline_text.py +106 -0
  184. agent_evolve/domain/insight.py +27 -0
  185. agent_evolve/domain/lineage.py +737 -0
  186. agent_evolve/domain/llm_task_queue.py +960 -0
  187. agent_evolve/domain/outcome.py +96 -0
  188. agent_evolve/domain/patch.py +854 -0
  189. agent_evolve/domain/typed_json.py +542 -0
  190. agent_evolve/domain/variation_space.py +158 -0
  191. agent_evolve/driver.py +1014 -0
  192. agent_evolve/harness/__init__.py +29 -0
  193. agent_evolve/harness/base.py +242 -0
  194. agent_evolve/harness/directives.py +163 -0
  195. agent_evolve/harness/generative_seal.py +479 -0
  196. agent_evolve/harness/registry.py +41 -0
  197. agent_evolve/infrastructure/__init__.py +39 -0
  198. agent_evolve/infrastructure/artifacts/__init__.py +6 -0
  199. agent_evolve/infrastructure/artifacts/_verification.py +67 -0
  200. agent_evolve/infrastructure/artifacts/filesystem.py +343 -0
  201. agent_evolve/infrastructure/artifacts/in_memory.py +73 -0
  202. agent_evolve/infrastructure/asyncio_runtime.py +109 -0
  203. agent_evolve/infrastructure/authored_runtime.py +188 -0
  204. agent_evolve/infrastructure/authored_worker.py +171 -0
  205. agent_evolve/infrastructure/clock.py +53 -0
  206. agent_evolve/infrastructure/events/__init__.py +6 -0
  207. agent_evolve/infrastructure/events/_validation.py +89 -0
  208. agent_evolve/infrastructure/events/in_memory.py +56 -0
  209. agent_evolve/infrastructure/events/jsonl.py +193 -0
  210. agent_evolve/infrastructure/exception_provenance.py +215 -0
  211. agent_evolve/infrastructure/ids.py +118 -0
  212. agent_evolve/infrastructure/lineage_codec.py +1836 -0
  213. agent_evolve/infrastructure/outcome_adaptive_phase_journal.py +170 -0
  214. agent_evolve/infrastructure/residual_headroom_journal.py +221 -0
  215. agent_evolve/infrastructure/resource_lease.py +370 -0
  216. agent_evolve/infrastructure/sanitization/__init__.py +8 -0
  217. agent_evolve/infrastructure/sanitization/strict_json.py +484 -0
  218. agent_evolve/infrastructure/sequential_phase_journal.py +170 -0
  219. agent_evolve/infrastructure/stream_liveness.py +383 -0
  220. agent_evolve/infrastructure/subprocess_boundary.py +136 -0
  221. agent_evolve/integrations/__init__.py +1 -0
  222. agent_evolve/integrations/botorch/__init__.py +28 -0
  223. agent_evolve/integrations/botorch/finite_qlognehvi.py +190 -0
  224. agent_evolve/integrations/botorch/finite_qlognehvi_batch.py +155 -0
  225. agent_evolve/integrations/botorch/finite_qlognehvi_batch_identity.py +20 -0
  226. agent_evolve/integrations/botorch/finite_qlognehvi_batch_worker.py +55 -0
  227. agent_evolve/integrations/botorch/finite_qlognehvi_identity.py +22 -0
  228. agent_evolve/integrations/botorch/finite_qlognehvi_worker.py +55 -0
  229. agent_evolve/integrations/botorch/subprocess_qlognehvi.py +261 -0
  230. agent_evolve/integrations/botorch/subprocess_qlognehvi_batch.py +273 -0
  231. agent_evolve/integrations/completion.py +242 -0
  232. agent_evolve/integrations/pydantic_ai/__init__.py +441 -0
  233. agent_evolve/integrations/pydantic_ai/action_forecast.py +1068 -0
  234. agent_evolve/integrations/pydantic_ai/agentic_generator.py +2308 -0
  235. agent_evolve/integrations/pydantic_ai/async_generator.py +1604 -0
  236. agent_evolve/integrations/pydantic_ai/boundary_codec.py +1526 -0
  237. agent_evolve/integrations/pydantic_ai/calibrated_portfolio_campaign.py +756 -0
  238. agent_evolve/integrations/pydantic_ai/calibrated_portfolio_selection.py +7537 -0
  239. agent_evolve/integrations/pydantic_ai/campaign_acquisition.py +609 -0
  240. agent_evolve/integrations/pydantic_ai/execution_binding.py +138 -0
  241. agent_evolve/integrations/pydantic_ai/forecast_geometry_action_committee.py +217 -0
  242. agent_evolve/integrations/pydantic_ai/harness.py +159 -0
  243. agent_evolve/integrations/pydantic_ai/heterogeneous_model_execution.py +306 -0
  244. agent_evolve/integrations/pydantic_ai/hierarchical_residual_adaptive_semantic_view.py +179 -0
  245. agent_evolve/integrations/pydantic_ai/json_schema_dialect.py +108 -0
  246. agent_evolve/integrations/pydantic_ai/materialized_hierarchical_residual_expert.py +952 -0
  247. agent_evolve/integrations/pydantic_ai/materialized_portfolio_judge.py +520 -0
  248. agent_evolve/integrations/pydantic_ai/model_execution_profile.py +659 -0
  249. agent_evolve/integrations/pydantic_ai/outbound_request_manifest.py +1170 -0
  250. agent_evolve/integrations/pydantic_ai/portable_residual_consequence_features.py +575 -0
  251. agent_evolve/integrations/pydantic_ai/portfolio_selection.py +422 -0
  252. agent_evolve/integrations/pydantic_ai/progress_aware_openrouter.py +416 -0
  253. agent_evolve/integrations/pydantic_ai/provider_attempt_join.py +1523 -0
  254. agent_evolve/integrations/pydantic_ai/provider_free_calibrated_runner.py +607 -0
  255. agent_evolve/integrations/pydantic_ai/queued_runner.py +2634 -0
  256. agent_evolve/integrations/pydantic_ai/reconciled_residual_reachability.py +1417 -0
  257. agent_evolve/integrations/pydantic_ai/residual_forecast_geometry.py +445 -0
  258. agent_evolve/integrations/pydantic_ai/residual_reachability.py +674 -0
  259. agent_evolve/integrations/pydantic_ai/residual_semantic_cells.py +239 -0
  260. agent_evolve/integrations/pydantic_ai/sealed_output_replay.py +1068 -0
  261. agent_evolve/integrations/pydantic_ai/semantic_coverage_residual_portfolio.py +770 -0
  262. agent_evolve/integrations/pydantic_ai/semantic_decision_replay.py +383 -0
  263. agent_evolve/integrations/pydantic_ai/support_adaptive_residual_portfolio.py +135 -0
  264. agent_evolve/integrations/pydantic_ai/trusted_residual_prompt_context.py +143 -0
  265. agent_evolve/integrations/pydantic_ai/validated_openrouter_model.py +107 -0
  266. agent_evolve/integrations/pymoo_adapter.py +242 -0
  267. agent_evolve/policies/__init__.py +17 -0
  268. agent_evolve/policies/check.py +469 -0
  269. agent_evolve/policies/emit_scaffold.py +451 -0
  270. agent_evolve/policies/feedback/__init__.py +37 -0
  271. agent_evolve/policies/feedback/held_out_asn.py +1325 -0
  272. agent_evolve/policies/genetic.py +607 -0
  273. agent_evolve/policies/llm_backoff.py +183 -0
  274. agent_evolve/policies/llm_chooser.py +226 -0
  275. agent_evolve/policies/llm_generator.py +1760 -0
  276. agent_evolve/policies/llm_init.py +267 -0
  277. agent_evolve/policies/llm_operator.py +109 -0
  278. agent_evolve/policies/llm_prior.py +194 -0
  279. agent_evolve/policies/llm_surrogate.py +334 -0
  280. agent_evolve/policies/measurement_evidence.py +704 -0
  281. agent_evolve/policies/memory/__init__.py +223 -0
  282. agent_evolve/policies/memory/balanced_subset_blocks.py +707 -0
  283. agent_evolve/policies/memory/compatibility_matching.py +593 -0
  284. agent_evolve/policies/memory/global_falsification.py +1841 -0
  285. agent_evolve/policies/memory/prompt_shape.py +503 -0
  286. agent_evolve/policies/memory/randomized_subset.py +714 -0
  287. agent_evolve/policies/memory/staged_causal.py +1270 -0
  288. agent_evolve/policies/memory/treatment_compliance.py +759 -0
  289. agent_evolve/policies/objective_resolution/__init__.py +17 -0
  290. agent_evolve/policies/objective_resolution/fixed_grid.py +364 -0
  291. agent_evolve/policies/operator_portfolio.py +407 -0
  292. agent_evolve/policies/reguidance.py +1133 -0
  293. agent_evolve/policies/reward/__init__.py +83 -0
  294. agent_evolve/policies/reward/affine_candidate_consequence.py +156 -0
  295. agent_evolve/policies/reward/affine_candidate_consequence_3d.py +159 -0
  296. agent_evolve/policies/reward/affine_hypervolume.py +490 -0
  297. agent_evolve/policies/reward/affine_hypervolume_3d.py +567 -0
  298. agent_evolve/policies/reward/contextual_marginal_utility.py +318 -0
  299. agent_evolve/policies/reward/frozen_archive.py +360 -0
  300. agent_evolve/policies/reward/frozen_wave_archive.py +368 -0
  301. agent_evolve/policies/search_state.py +208 -0
  302. agent_evolve/policies/selection/__init__.py +345 -0
  303. agent_evolve/policies/selection/acquisition_certified_slate.py +684 -0
  304. agent_evolve/policies/selection/affine_frontier_context.py +330 -0
  305. agent_evolve/policies/selection/affine_frontier_target.py +473 -0
  306. agent_evolve/policies/selection/archive_elite.py +1346 -0
  307. agent_evolve/policies/selection/calibrated_portfolio_binding.py +640 -0
  308. agent_evolve/policies/selection/calibrated_slate.py +1394 -0
  309. agent_evolve/policies/selection/calibrated_slate_codec.py +579 -0
  310. agent_evolve/policies/selection/common_candidate_pool.py +685 -0
  311. agent_evolve/policies/selection/diagnostic_sampling.py +319 -0
  312. agent_evolve/policies/selection/disjoint_pairs.py +479 -0
  313. agent_evolve/policies/selection/elite_explorer.py +719 -0
  314. agent_evolve/policies/selection/finite_action.py +187 -0
  315. agent_evolve/policies/selection/finite_option_prompt_projection.py +377 -0
  316. agent_evolve/policies/selection/finite_palette_evidence.py +247 -0
  317. agent_evolve/policies/selection/forecast_calibration.py +922 -0
  318. agent_evolve/policies/selection/frontier_probe_slate.py +814 -0
  319. agent_evolve/policies/selection/frozen_archive_pairs.py +762 -0
  320. agent_evolve/policies/selection/full_support_slate.py +91 -0
  321. agent_evolve/policies/selection/meaningful_direction.py +240 -0
  322. agent_evolve/policies/selection/memory_dose_feasibility.py +259 -0
  323. agent_evolve/policies/selection/model_anchored_slate.py +826 -0
  324. agent_evolve/policies/selection/phenotype_recourse.py +979 -0
  325. agent_evolve/policies/selection/proposal_support.py +368 -0
  326. agent_evolve/policies/selection/random_portfolio.py +254 -0
  327. agent_evolve/policies/selection/regret_bounded_slate.py +1084 -0
  328. agent_evolve/policies/selection/residual_frontier.py +463 -0
  329. agent_evolve/policies/selection/residual_frontier_target.py +605 -0
  330. agent_evolve/policies/selection/structural_posterior_slate.py +1571 -0
  331. agent_evolve/policies/selection/target_conditioned_allocator.py +648 -0
  332. agent_evolve/policies/selection/target_conditioned_features.py +812 -0
  333. agent_evolve/policies/selection/target_conditioned_prequential.py +1527 -0
  334. agent_evolve/policies/selection/task_keyed_palette.py +906 -0
  335. agent_evolve/policies/semantics.py +147 -0
  336. agent_evolve/policies/structure.py +362 -0
  337. agent_evolve/policies/structured_output_budget.py +62 -0
  338. agent_evolve/policies/surrogate.py +696 -0
  339. agent_evolve/policies/variation/__init__.py +1 -0
  340. agent_evolve/policies/variation/compositional_finite_catalog.py +426 -0
  341. agent_evolve/policies/variation/crossover_inheritance.py +575 -0
  342. agent_evolve/policies/variation/disjoint_recombination.py +611 -0
  343. agent_evolve/policies/variation/exact_composition_capacity.py +214 -0
  344. agent_evolve/policies/variation/exact_parent_crossover.py +950 -0
  345. agent_evolve/policies/variation/multiscale_restart_catalog.py +372 -0
  346. agent_evolve/policies/variation/source_union_finite_catalog.py +403 -0
  347. agent_evolve/policies/variation/typed_patch.py +1981 -0
  348. agent_evolve/policies/weighted_prior.py +394 -0
  349. agent_evolve/ports/__init__.py +383 -0
  350. agent_evolve/ports/action_allocation.py +733 -0
  351. agent_evolve/ports/action_allocation_frame.py +1153 -0
  352. agent_evolve/ports/action_allocation_frame_commit.py +294 -0
  353. agent_evolve/ports/action_allocation_frame_commit_v3.py +432 -0
  354. agent_evolve/ports/action_allocation_frame_v3.py +995 -0
  355. agent_evolve/ports/action_forecast.py +1568 -0
  356. agent_evolve/ports/action_metric_projection.py +165 -0
  357. agent_evolve/ports/agentic_generator.py +1561 -0
  358. agent_evolve/ports/archive_context.py +136 -0
  359. agent_evolve/ports/artifact_sanitizer.py +44 -0
  360. agent_evolve/ports/artifact_store.py +225 -0
  361. agent_evolve/ports/clock.py +13 -0
  362. agent_evolve/ports/contextual_search_allocation.py +827 -0
  363. agent_evolve/ports/decision_metric_projection.py +258 -0
  364. agent_evolve/ports/event_store.py +55 -0
  365. agent_evolve/ports/executable_hypothesis.py +557 -0
  366. agent_evolve/ports/finite_acquisition.py +377 -0
  367. agent_evolve/ports/finite_acquisition_batch.py +296 -0
  368. agent_evolve/ports/finite_acquisition_batch_json.py +164 -0
  369. agent_evolve/ports/finite_acquisition_json.py +247 -0
  370. agent_evolve/ports/finite_acquisition_space.py +168 -0
  371. agent_evolve/ports/finite_action_selection.py +348 -0
  372. agent_evolve/ports/finite_action_set.py +256 -0
  373. agent_evolve/ports/frontier_target.py +396 -0
  374. agent_evolve/ports/generation_failure.py +43 -0
  375. agent_evolve/ports/hard_feasibility.py +233 -0
  376. agent_evolve/ports/id_factory.py +34 -0
  377. agent_evolve/ports/llm_task_queue.py +93 -0
  378. agent_evolve/ports/objective_resolution.py +419 -0
  379. agent_evolve/ports/paired_allocation_comparison.py +401 -0
  380. agent_evolve/ports/paired_block_schedule.py +475 -0
  381. agent_evolve/ports/parent_measurement.py +336 -0
  382. agent_evolve/ports/portfolio_memory_dose.py +643 -0
  383. agent_evolve/ports/portfolio_selection.py +3169 -0
  384. agent_evolve/ports/postcommit_rank_authority.py +467 -0
  385. agent_evolve/ports/presented_action_evidence.py +794 -0
  386. agent_evolve/ports/resource_lease.py +162 -0
  387. agent_evolve/ports/structured_generator.py +734 -0
  388. agent_evolve/ports/structured_output_budget.py +120 -0
  389. agent_evolve/ports/subprocess_boundary.py +138 -0
  390. agent_evolve/ports/treatment_assignment.py +466 -0
  391. agent_evolve/ports/variation_catalog.py +76 -0
  392. agent_evolve/ports/variation_source.py +226 -0
  393. agent_evolve/proposal_mode.py +157 -0
  394. agent_evolve/proposers/__init__.py +10 -0
  395. agent_evolve/proposers/random_proposer.py +188 -0
  396. agent_evolve/provider_accounting.py +163 -0
  397. agent_evolve/py.typed +0 -0
  398. agent_evolve/reference_method.py +1570 -0
  399. agent_evolve/session/__init__.py +11 -0
  400. agent_evolve/session/authorship.py +864 -0
  401. agent_evolve/session/evaluate.py +236 -0
  402. agent_evolve/session/fidelity.py +237 -0
  403. agent_evolve/session/genetic_loop.py +742 -0
  404. agent_evolve/session/loop.py +803 -0
  405. agent_evolve/session/screening.py +671 -0
  406. agent_evolve/settings.py +376 -0
  407. agent_evolve/workload_kit.py +368 -0
  408. agent_evolve/workload_prompt.py +398 -0
  409. agentevolve_optimizer-0.5.0.dist-info/METADATA +599 -0
  410. agentevolve_optimizer-0.5.0.dist-info/RECORD +414 -0
  411. agentevolve_optimizer-0.5.0.dist-info/WHEEL +5 -0
  412. agentevolve_optimizer-0.5.0.dist-info/entry_points.txt +2 -0
  413. agentevolve_optimizer-0.5.0.dist-info/licenses/LICENSE +21 -0
  414. agentevolve_optimizer-0.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,377 @@
1
+ """Workload-neutral ask/tell boundary for finite black-box acquisition.
2
+
3
+ The core optimizer owns no feature encoding and no configuration materializer.
4
+ A workload adapter exposes a finite set of already legal candidate identities
5
+ and normalized feature rows. An optional acquisition integration selects a
6
+ batch by identity; trusted adapter code maps those identities back to sealed
7
+ configurations. This permits Bayesian, evolutionary, and learned acquisition
8
+ experts to share one boundary without importing workload schemas into the
9
+ application layer.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import hashlib
15
+ import json
16
+ import math
17
+ import re
18
+ from dataclasses import dataclass, field
19
+ from typing import Protocol, runtime_checkable
20
+
21
+
22
+ _TOKEN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,511}$")
23
+ _REQUEST_DOMAIN = b"agent-evolve:finite-acquisition-request:v1\x00"
24
+ _DECISION_DOMAIN = b"agent-evolve:finite-acquisition-decision:v1\x00"
25
+
26
+
27
+ def _canonical_json(value: object) -> bytes:
28
+ return json.dumps(
29
+ value,
30
+ allow_nan=False,
31
+ ensure_ascii=True,
32
+ separators=(",", ":"),
33
+ sort_keys=True,
34
+ ).encode("ascii", errors="strict")
35
+
36
+
37
+ def _require_token(value: str, *, name: str) -> None:
38
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
39
+ raise ValueError(f"{name} must use the closed token grammar")
40
+
41
+
42
+ def _require_sha256(value: str, *, name: str) -> None:
43
+ if (
44
+ type(value) is not str
45
+ or len(value) != 64
46
+ or any(character not in "0123456789abcdef" for character in value)
47
+ ):
48
+ raise ValueError(f"{name} must be a lowercase SHA-256")
49
+
50
+
51
+ def _finite(value: float, *, name: str) -> None:
52
+ if type(value) is not float or not math.isfinite(value):
53
+ raise TypeError(f"{name} must be a finite canonical float")
54
+
55
+
56
+ @dataclass(frozen=True, slots=True)
57
+ class FiniteAcquisitionObjective:
58
+ """One raw objective and the fixed affine frame used by acquisition."""
59
+
60
+ metric_id: str
61
+ sense: str
62
+ ideal: float
63
+ reference: float
64
+
65
+ def __post_init__(self) -> None:
66
+ _require_token(self.metric_id, name="metric_id")
67
+ if self.sense not in ("min", "max"):
68
+ raise ValueError("sense must be 'min' or 'max'")
69
+ _finite(self.ideal, name="ideal")
70
+ _finite(self.reference, name="reference")
71
+ if self.ideal == self.reference:
72
+ raise ValueError("ideal and reference must be distinct")
73
+ if self.sense == "min" and self.ideal >= self.reference:
74
+ raise ValueError("minimization ideal must be below reference")
75
+ if self.sense == "max" and self.ideal <= self.reference:
76
+ raise ValueError("maximization ideal must be above reference")
77
+
78
+ def maximize_value(self, raw_value: float) -> float:
79
+ """Map the fixed reference to zero and the ideal to one."""
80
+
81
+ self.__post_init__()
82
+ _finite(raw_value, name="raw_value")
83
+ if self.sense == "min":
84
+ return (self.reference - raw_value) / (self.reference - self.ideal)
85
+ return (raw_value - self.reference) / (self.ideal - self.reference)
86
+
87
+ def to_record(self) -> dict[str, object]:
88
+ self.__post_init__()
89
+ return {
90
+ "metric_id": self.metric_id,
91
+ "sense": self.sense,
92
+ "ideal_hex": self.ideal.hex(),
93
+ "reference_hex": self.reference.hex(),
94
+ }
95
+
96
+
97
+ @dataclass(frozen=True, slots=True)
98
+ class FiniteAcquisitionCandidate:
99
+ """One legal candidate row; the configuration remains adapter-owned."""
100
+
101
+ candidate_id: str
102
+ configuration_sha256: str
103
+ features: tuple[float, ...]
104
+
105
+ def __post_init__(self) -> None:
106
+ _require_token(self.candidate_id, name="candidate_id")
107
+ _require_sha256(self.configuration_sha256, name="configuration_sha256")
108
+ if type(self.features) is not tuple or not self.features:
109
+ raise ValueError("features must be a non-empty exact tuple")
110
+ for value in self.features:
111
+ _finite(value, name="feature")
112
+ if not 0.0 <= value <= 1.0:
113
+ raise ValueError("features must lie in [0,1]")
114
+
115
+ def to_record(self) -> dict[str, object]:
116
+ self.__post_init__()
117
+ return {
118
+ "candidate_id": self.candidate_id,
119
+ "configuration_sha256": self.configuration_sha256,
120
+ "features_hex": [value.hex() for value in self.features],
121
+ }
122
+
123
+
124
+ @dataclass(frozen=True, slots=True)
125
+ class FiniteAcquisitionObservation:
126
+ """One prior real-evaluator result in the same feature frame."""
127
+
128
+ candidate_id: str
129
+ configuration_sha256: str
130
+ features: tuple[float, ...]
131
+ objectives: tuple[tuple[str, float], ...]
132
+
133
+ def __post_init__(self) -> None:
134
+ FiniteAcquisitionCandidate(
135
+ candidate_id=self.candidate_id,
136
+ configuration_sha256=self.configuration_sha256,
137
+ features=self.features,
138
+ )
139
+ if type(self.objectives) is not tuple or not self.objectives:
140
+ raise ValueError("objectives must be a non-empty exact tuple")
141
+ metric_ids: list[str] = []
142
+ for metric_id, value in self.objectives:
143
+ _require_token(metric_id, name="objective metric_id")
144
+ _finite(value, name="objective value")
145
+ metric_ids.append(metric_id)
146
+ if tuple(metric_ids) != tuple(sorted(set(metric_ids))):
147
+ raise ValueError("objectives must be unique and metric-sorted")
148
+
149
+ def to_record(self) -> dict[str, object]:
150
+ self.__post_init__()
151
+ return {
152
+ "candidate_id": self.candidate_id,
153
+ "configuration_sha256": self.configuration_sha256,
154
+ "features_hex": [value.hex() for value in self.features],
155
+ "objectives": [
156
+ {"metric_id": metric_id, "value_hex": value.hex()}
157
+ for metric_id, value in self.objectives
158
+ ],
159
+ }
160
+
161
+
162
+ @dataclass(frozen=True, slots=True, eq=False)
163
+ class FiniteAcquisitionRequest:
164
+ """Immutable finite-pool ask request at one authenticated cutoff."""
165
+
166
+ campaign_scope_sha256: str
167
+ cutoff_index: int
168
+ batch_size: int
169
+ seed: int
170
+ objectives: tuple[FiniteAcquisitionObjective, ...]
171
+ observations: tuple[FiniteAcquisitionObservation, ...]
172
+ candidates: tuple[FiniteAcquisitionCandidate, ...]
173
+ request_sha256: str = field(init=False, default="")
174
+
175
+ def __post_init__(self) -> None:
176
+ _require_sha256(self.campaign_scope_sha256, name="campaign_scope_sha256")
177
+ if type(self.cutoff_index) is not int or self.cutoff_index < 1:
178
+ raise ValueError("cutoff_index must be positive")
179
+ if type(self.batch_size) is not int or self.batch_size < 1:
180
+ raise ValueError("batch_size must be positive")
181
+ if type(self.seed) is not int or self.seed < 0:
182
+ raise ValueError("seed must be a non-negative exact integer")
183
+ if type(self.objectives) is not tuple or len(self.objectives) < 2:
184
+ raise ValueError("multi-objective acquisition requires at least two axes")
185
+ for axis in self.objectives:
186
+ if type(axis) is not FiniteAcquisitionObjective:
187
+ raise TypeError("objectives must contain exact axes")
188
+ axis.__post_init__()
189
+ metric_ids = tuple(axis.metric_id for axis in self.objectives)
190
+ if metric_ids != tuple(sorted(set(metric_ids))):
191
+ raise ValueError("objective axes must be unique and metric-sorted")
192
+ if type(self.observations) is not tuple or not self.observations:
193
+ raise ValueError("observations must be a non-empty exact tuple")
194
+ if type(self.candidates) is not tuple or len(self.candidates) < self.batch_size:
195
+ raise ValueError("candidate pool cannot underfill the requested batch")
196
+ dimensions: set[int] = set()
197
+ observed_ids: set[str] = set()
198
+ observed_hashes: set[str] = set()
199
+ for observation in self.observations:
200
+ if type(observation) is not FiniteAcquisitionObservation:
201
+ raise TypeError("observations must contain exact rows")
202
+ observation.__post_init__()
203
+ if tuple(value[0] for value in observation.objectives) != metric_ids:
204
+ raise ValueError("observation objective axes differ from request")
205
+ dimensions.add(len(observation.features))
206
+ if (
207
+ observation.candidate_id in observed_ids
208
+ or observation.configuration_sha256 in observed_hashes
209
+ ):
210
+ raise ValueError("observations must have unique IDs and configurations")
211
+ observed_ids.add(observation.candidate_id)
212
+ observed_hashes.add(observation.configuration_sha256)
213
+ candidate_ids: set[str] = set()
214
+ candidate_hashes: set[str] = set()
215
+ candidate_features: set[tuple[float, ...]] = set()
216
+ for candidate in self.candidates:
217
+ if type(candidate) is not FiniteAcquisitionCandidate:
218
+ raise TypeError("candidates must contain exact rows")
219
+ candidate.__post_init__()
220
+ dimensions.add(len(candidate.features))
221
+ if (
222
+ candidate.candidate_id in candidate_ids
223
+ or candidate.configuration_sha256 in candidate_hashes
224
+ or candidate.features in candidate_features
225
+ ):
226
+ raise ValueError("candidate IDs, configurations, and features must be unique")
227
+ if candidate.configuration_sha256 in observed_hashes:
228
+ raise ValueError("candidate pool repeats an evaluated configuration")
229
+ candidate_ids.add(candidate.candidate_id)
230
+ candidate_hashes.add(candidate.configuration_sha256)
231
+ candidate_features.add(candidate.features)
232
+ if len(dimensions) != 1:
233
+ raise ValueError("all feature rows must share one dimension")
234
+ computed = hashlib.sha256(
235
+ _REQUEST_DOMAIN + _canonical_json(self._unsigned_record())
236
+ ).hexdigest()
237
+ if self.request_sha256 not in ("", computed):
238
+ raise ValueError("request_sha256 does not authenticate the request")
239
+ object.__setattr__(self, "request_sha256", computed)
240
+
241
+ def _unsigned_record(self) -> dict[str, object]:
242
+ return {
243
+ "schema_version": 1,
244
+ "campaign_scope_sha256": self.campaign_scope_sha256,
245
+ "cutoff_index": self.cutoff_index,
246
+ "batch_size": self.batch_size,
247
+ "seed": self.seed,
248
+ "objectives": [axis.to_record() for axis in self.objectives],
249
+ "observations": [value.to_record() for value in self.observations],
250
+ "candidates": [value.to_record() for value in self.candidates],
251
+ }
252
+
253
+ def to_record(self) -> dict[str, object]:
254
+ self.__post_init__()
255
+ return {**self._unsigned_record(), "request_sha256": self.request_sha256}
256
+
257
+ def __eq__(self, other: object) -> bool:
258
+ return (
259
+ type(other) is FiniteAcquisitionRequest
260
+ and self.request_sha256 == other.request_sha256
261
+ )
262
+
263
+ __hash__ = None
264
+
265
+
266
+ @dataclass(frozen=True, slots=True)
267
+ class FiniteAcquisitionSelection:
268
+ candidate_id: str
269
+ configuration_sha256: str
270
+ acquisition_value: float
271
+
272
+ def __post_init__(self) -> None:
273
+ _require_token(self.candidate_id, name="candidate_id")
274
+ _require_sha256(self.configuration_sha256, name="configuration_sha256")
275
+ _finite(self.acquisition_value, name="acquisition_value")
276
+
277
+ def to_record(self) -> dict[str, object]:
278
+ self.__post_init__()
279
+ return {
280
+ "candidate_id": self.candidate_id,
281
+ "configuration_sha256": self.configuration_sha256,
282
+ "acquisition_value_hex": self.acquisition_value.hex(),
283
+ }
284
+
285
+
286
+ @dataclass(frozen=True, slots=True, eq=False)
287
+ class FiniteAcquisitionDecision:
288
+ request_sha256: str
289
+ policy_id: str
290
+ policy_version: int
291
+ policy_definition_sha256: str
292
+ selected: tuple[FiniteAcquisitionSelection, ...]
293
+ diagnostics: tuple[tuple[str, str], ...] = ()
294
+ decision_sha256: str = field(init=False, default="")
295
+
296
+ def __post_init__(self) -> None:
297
+ _require_sha256(self.request_sha256, name="request_sha256")
298
+ _require_token(self.policy_id, name="policy_id")
299
+ if type(self.policy_version) is not int or self.policy_version < 1:
300
+ raise ValueError("policy_version must be positive")
301
+ _require_sha256(
302
+ self.policy_definition_sha256,
303
+ name="policy_definition_sha256",
304
+ )
305
+ if type(self.selected) is not tuple or not self.selected:
306
+ raise ValueError("selected must be a non-empty exact tuple")
307
+ for value in self.selected:
308
+ if type(value) is not FiniteAcquisitionSelection:
309
+ raise TypeError("selected must contain exact rows")
310
+ value.__post_init__()
311
+ if len({value.candidate_id for value in self.selected}) != len(self.selected):
312
+ raise ValueError("selected candidates must be unique")
313
+ if type(self.diagnostics) is not tuple:
314
+ raise TypeError("diagnostics must be an exact tuple")
315
+ diagnostic_keys: list[str] = []
316
+ for key, value in self.diagnostics:
317
+ _require_token(key, name="diagnostic key")
318
+ if type(value) is not str or not value.isascii():
319
+ raise ValueError("diagnostic values must be ASCII strings")
320
+ diagnostic_keys.append(key)
321
+ if tuple(diagnostic_keys) != tuple(sorted(set(diagnostic_keys))):
322
+ raise ValueError("diagnostics must be unique and key-sorted")
323
+ computed = hashlib.sha256(
324
+ _DECISION_DOMAIN + _canonical_json(self._unsigned_record())
325
+ ).hexdigest()
326
+ if self.decision_sha256 not in ("", computed):
327
+ raise ValueError("decision_sha256 does not authenticate the decision")
328
+ object.__setattr__(self, "decision_sha256", computed)
329
+
330
+ def _unsigned_record(self) -> dict[str, object]:
331
+ return {
332
+ "schema_version": 1,
333
+ "request_sha256": self.request_sha256,
334
+ "policy": {
335
+ "policy_id": self.policy_id,
336
+ "policy_version": self.policy_version,
337
+ "definition_sha256": self.policy_definition_sha256,
338
+ },
339
+ "selected": [value.to_record() for value in self.selected],
340
+ "diagnostics": [
341
+ {"key": key, "value": value} for key, value in self.diagnostics
342
+ ],
343
+ }
344
+
345
+ def to_record(self) -> dict[str, object]:
346
+ self.__post_init__()
347
+ return {**self._unsigned_record(), "decision_sha256": self.decision_sha256}
348
+
349
+ def __eq__(self, other: object) -> bool:
350
+ return (
351
+ type(other) is FiniteAcquisitionDecision
352
+ and self.decision_sha256 == other.decision_sha256
353
+ )
354
+
355
+ __hash__ = None
356
+
357
+
358
+ @runtime_checkable
359
+ class FiniteAcquisitionPolicy(Protocol):
360
+ """Select identities from a sealed legal pool using prior observations."""
361
+
362
+ policy_id: str
363
+ policy_version: int
364
+ definition_sha256: str
365
+
366
+ def select(self, request: FiniteAcquisitionRequest) -> FiniteAcquisitionDecision: ...
367
+
368
+
369
+ __all__ = [
370
+ "FiniteAcquisitionCandidate",
371
+ "FiniteAcquisitionDecision",
372
+ "FiniteAcquisitionObjective",
373
+ "FiniteAcquisitionObservation",
374
+ "FiniteAcquisitionPolicy",
375
+ "FiniteAcquisitionRequest",
376
+ "FiniteAcquisitionSelection",
377
+ ]
@@ -0,0 +1,296 @@
1
+ """Workload-neutral scoring boundary for finite acquisition slates.
2
+
3
+ The ordinary finite-acquisition port asks a numerical optimizer to construct a
4
+ batch. AgentEvolve also needs a stricter comparison primitive: score several
5
+ already-enumerated, equally sized slates under one fitted model and one Monte
6
+ Carlo draw. Keeping that operation behind a port lets an application policy
7
+ retain an incumbent optimizer slate while testing agent-proposed alternatives
8
+ without importing BoTorch or a workload schema into the core.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import hashlib
14
+ import json
15
+ import math
16
+ import re
17
+ from dataclasses import dataclass, field
18
+ from typing import Protocol, runtime_checkable
19
+
20
+ from agent_evolve.domain.patch import require_sha256
21
+ from agent_evolve.ports.finite_acquisition import (
22
+ FiniteAcquisitionCandidate,
23
+ FiniteAcquisitionObjective,
24
+ FiniteAcquisitionObservation,
25
+ FiniteAcquisitionRequest,
26
+ )
27
+
28
+
29
+ _TOKEN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,511}$")
30
+ _REQUEST_DOMAIN = b"agent-evolve:finite-acquisition-batch-score-request:v1\x00"
31
+ _DECISION_DOMAIN = b"agent-evolve:finite-acquisition-batch-score-decision:v1\x00"
32
+
33
+
34
+ def _canonical_json(value: object) -> bytes:
35
+ return json.dumps(
36
+ value,
37
+ allow_nan=False,
38
+ ensure_ascii=True,
39
+ separators=(",", ":"),
40
+ sort_keys=True,
41
+ ).encode("ascii", errors="strict")
42
+
43
+
44
+ def _require_token(value: str, *, name: str) -> None:
45
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
46
+ raise ValueError(f"{name} must use the closed token grammar")
47
+
48
+
49
+ def _finite(value: float, *, name: str) -> None:
50
+ if type(value) is not float or not math.isfinite(value):
51
+ raise TypeError(f"{name} must be a finite exact float")
52
+
53
+
54
+ @dataclass(frozen=True, slots=True)
55
+ class FiniteAcquisitionSlate:
56
+ """One unordered, canonical candidate-ID set to score jointly."""
57
+
58
+ candidate_ids: tuple[str, ...]
59
+
60
+ def __post_init__(self) -> None:
61
+ if type(self.candidate_ids) is not tuple or not self.candidate_ids:
62
+ raise ValueError("candidate_ids must be a non-empty exact tuple")
63
+ for value in self.candidate_ids:
64
+ _require_token(value, name="candidate_id")
65
+ if self.candidate_ids != tuple(sorted(set(self.candidate_ids))):
66
+ raise ValueError("candidate_ids must be unique and canonical")
67
+
68
+ def to_record(self) -> dict[str, object]:
69
+ self.__post_init__()
70
+ return {"candidate_ids": list(self.candidate_ids)}
71
+
72
+
73
+ @dataclass(frozen=True, slots=True, eq=False)
74
+ class FiniteAcquisitionBatchScoreRequest:
75
+ """Score a sealed family of equal-cardinality finite slates."""
76
+
77
+ campaign_scope_sha256: str
78
+ cutoff_index: int
79
+ seed: int
80
+ objectives: tuple[FiniteAcquisitionObjective, ...]
81
+ observations: tuple[FiniteAcquisitionObservation, ...]
82
+ candidates: tuple[FiniteAcquisitionCandidate, ...]
83
+ slates: tuple[FiniteAcquisitionSlate, ...]
84
+ request_sha256: str = field(init=False, default="")
85
+
86
+ def __post_init__(self) -> None:
87
+ require_sha256(self.campaign_scope_sha256, "campaign_scope_sha256")
88
+ if type(self.cutoff_index) is not int or self.cutoff_index < 1:
89
+ raise ValueError("cutoff_index must be positive")
90
+ if type(self.seed) is not int or self.seed < 0:
91
+ raise ValueError("seed must be a non-negative exact integer")
92
+ if type(self.slates) is not tuple or not self.slates:
93
+ raise ValueError("slates must be a non-empty exact tuple")
94
+ for value in self.slates:
95
+ if type(value) is not FiniteAcquisitionSlate:
96
+ raise TypeError("slates must contain exact finite slates")
97
+ value.__post_init__()
98
+ if self.slates != tuple(
99
+ sorted(set(self.slates), key=lambda value: value.candidate_ids)
100
+ ):
101
+ raise ValueError("slates must be unique and canonically ordered")
102
+ widths = {len(value.candidate_ids) for value in self.slates}
103
+ if len(widths) != 1:
104
+ raise ValueError("all acquisition slates must have one cardinality")
105
+ batch_size = next(iter(widths))
106
+ # Reuse the ordinary ask boundary as the single validation law for
107
+ # objectives, observations, features, and candidate uniqueness.
108
+ base = FiniteAcquisitionRequest(
109
+ campaign_scope_sha256=self.campaign_scope_sha256,
110
+ cutoff_index=self.cutoff_index,
111
+ batch_size=batch_size,
112
+ seed=self.seed,
113
+ objectives=self.objectives,
114
+ observations=self.observations,
115
+ candidates=self.candidates,
116
+ )
117
+ available = {value.candidate_id for value in base.candidates}
118
+ if any(not set(value.candidate_ids) <= available for value in self.slates):
119
+ raise ValueError("an acquisition slate escapes the sealed candidate pool")
120
+ computed = hashlib.sha256(
121
+ _REQUEST_DOMAIN + _canonical_json(self._unsigned_record())
122
+ ).hexdigest()
123
+ if self.request_sha256 not in ("", computed):
124
+ raise ValueError("request_sha256 does not authenticate the request")
125
+ object.__setattr__(self, "request_sha256", computed)
126
+
127
+ @property
128
+ def batch_size(self) -> int:
129
+ return len(self.slates[0].candidate_ids)
130
+
131
+ @property
132
+ def base_request(self) -> FiniteAcquisitionRequest:
133
+ """The ordinary finite-acquisition inputs shared by every slate."""
134
+
135
+ return FiniteAcquisitionRequest(
136
+ campaign_scope_sha256=self.campaign_scope_sha256,
137
+ cutoff_index=self.cutoff_index,
138
+ batch_size=self.batch_size,
139
+ seed=self.seed,
140
+ objectives=self.objectives,
141
+ observations=self.observations,
142
+ candidates=self.candidates,
143
+ )
144
+
145
+ def _unsigned_record(self) -> dict[str, object]:
146
+ return {
147
+ "schema_version": 1,
148
+ "base_request": self.base_request.to_record(),
149
+ "slates": [value.to_record() for value in self.slates],
150
+ }
151
+
152
+ def to_record(self) -> dict[str, object]:
153
+ self.__post_init__()
154
+ return {**self._unsigned_record(), "request_sha256": self.request_sha256}
155
+
156
+ def __eq__(self, other: object) -> bool:
157
+ return (
158
+ type(other) is FiniteAcquisitionBatchScoreRequest
159
+ and self.request_sha256 == other.request_sha256
160
+ )
161
+
162
+ __hash__ = None
163
+
164
+
165
+ @dataclass(frozen=True, slots=True)
166
+ class FiniteAcquisitionSlateScore:
167
+ slate: FiniteAcquisitionSlate
168
+ log_acquisition_value: float
169
+
170
+ def __post_init__(self) -> None:
171
+ if type(self.slate) is not FiniteAcquisitionSlate:
172
+ raise TypeError("slate must be an exact FiniteAcquisitionSlate")
173
+ self.slate.__post_init__()
174
+ _finite(self.log_acquisition_value, name="log_acquisition_value")
175
+
176
+ def to_record(self) -> dict[str, object]:
177
+ self.__post_init__()
178
+ return {
179
+ "slate": self.slate.to_record(),
180
+ "log_acquisition_value_hex": self.log_acquisition_value.hex(),
181
+ }
182
+
183
+
184
+ @dataclass(frozen=True, slots=True, eq=False)
185
+ class FiniteAcquisitionBatchScoreDecision:
186
+ request_sha256: str
187
+ policy_id: str
188
+ policy_version: int
189
+ policy_definition_sha256: str
190
+ scores: tuple[FiniteAcquisitionSlateScore, ...]
191
+ diagnostics: tuple[tuple[str, str], ...] = ()
192
+ decision_sha256: str = field(init=False, default="")
193
+
194
+ def __post_init__(self) -> None:
195
+ require_sha256(self.request_sha256, "request_sha256")
196
+ _require_token(self.policy_id, name="policy_id")
197
+ if type(self.policy_version) is not int or self.policy_version < 1:
198
+ raise ValueError("policy_version must be positive")
199
+ require_sha256(
200
+ self.policy_definition_sha256,
201
+ "policy_definition_sha256",
202
+ )
203
+ if type(self.scores) is not tuple or not self.scores:
204
+ raise ValueError("scores must be a non-empty exact tuple")
205
+ for value in self.scores:
206
+ if type(value) is not FiniteAcquisitionSlateScore:
207
+ raise TypeError("scores must contain exact slate scores")
208
+ value.__post_init__()
209
+ slates = tuple(value.slate for value in self.scores)
210
+ if slates != tuple(sorted(set(slates), key=lambda value: value.candidate_ids)):
211
+ raise ValueError("score slates must be unique and canonical")
212
+ if type(self.diagnostics) is not tuple:
213
+ raise TypeError("diagnostics must be an exact tuple")
214
+ keys: list[str] = []
215
+ for key, value in self.diagnostics:
216
+ _require_token(key, name="diagnostic key")
217
+ if type(value) is not str or not value.isascii():
218
+ raise ValueError("diagnostic values must be ASCII strings")
219
+ keys.append(key)
220
+ if tuple(keys) != tuple(sorted(set(keys))):
221
+ raise ValueError("diagnostics must be unique and key-sorted")
222
+ computed = hashlib.sha256(
223
+ _DECISION_DOMAIN + _canonical_json(self._unsigned_record())
224
+ ).hexdigest()
225
+ if self.decision_sha256 not in ("", computed):
226
+ raise ValueError("decision_sha256 does not authenticate the decision")
227
+ object.__setattr__(self, "decision_sha256", computed)
228
+
229
+ def _unsigned_record(self) -> dict[str, object]:
230
+ return {
231
+ "schema_version": 1,
232
+ "request_sha256": self.request_sha256,
233
+ "policy": {
234
+ "policy_id": self.policy_id,
235
+ "policy_version": self.policy_version,
236
+ "definition_sha256": self.policy_definition_sha256,
237
+ },
238
+ "scores": [value.to_record() for value in self.scores],
239
+ "diagnostics": [
240
+ {"key": key, "value": value} for key, value in self.diagnostics
241
+ ],
242
+ }
243
+
244
+ def to_record(self) -> dict[str, object]:
245
+ self.__post_init__()
246
+ return {**self._unsigned_record(), "decision_sha256": self.decision_sha256}
247
+
248
+ def __eq__(self, other: object) -> bool:
249
+ return (
250
+ type(other) is FiniteAcquisitionBatchScoreDecision
251
+ and self.decision_sha256 == other.decision_sha256
252
+ )
253
+
254
+ __hash__ = None
255
+
256
+
257
+ def validate_finite_acquisition_batch_score_decision(
258
+ request: FiniteAcquisitionBatchScoreRequest,
259
+ decision: FiniteAcquisitionBatchScoreDecision,
260
+ ) -> None:
261
+ """Require exact coverage of every requested slate and policy binding."""
262
+
263
+ if type(request) is not FiniteAcquisitionBatchScoreRequest:
264
+ raise TypeError("request must be exact")
265
+ request.__post_init__()
266
+ if type(decision) is not FiniteAcquisitionBatchScoreDecision:
267
+ raise TypeError("decision must be exact")
268
+ decision.__post_init__()
269
+ if decision.request_sha256 != request.request_sha256:
270
+ raise ValueError("batch-score decision targets another request")
271
+ if tuple(value.slate for value in decision.scores) != request.slates:
272
+ raise ValueError("batch-score decision must cover every requested slate")
273
+
274
+
275
+ @runtime_checkable
276
+ class FiniteAcquisitionBatchScorePolicy(Protocol):
277
+ """Score fixed slates under one past-only acquisition realization."""
278
+
279
+ policy_id: str
280
+ policy_version: int
281
+ definition_sha256: str
282
+
283
+ def score(
284
+ self,
285
+ request: FiniteAcquisitionBatchScoreRequest,
286
+ ) -> FiniteAcquisitionBatchScoreDecision: ...
287
+
288
+
289
+ __all__ = [
290
+ "FiniteAcquisitionBatchScoreDecision",
291
+ "FiniteAcquisitionBatchScorePolicy",
292
+ "FiniteAcquisitionBatchScoreRequest",
293
+ "FiniteAcquisitionSlate",
294
+ "FiniteAcquisitionSlateScore",
295
+ "validate_finite_acquisition_batch_score_decision",
296
+ ]