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,906 @@
1
+ """Outcome-blind exposure floors over heterogeneous proposal sources.
2
+
3
+ The application core must be able to conserve genuinely different search
4
+ processes without learning what a "model", "provider", or workload is. This
5
+ module therefore exposes two small inverted APIs:
6
+
7
+ * a projection assigns every sealed materialized action to an opaque source
8
+ group; and
9
+ * an ordinary pre-evaluation score authority ranks candidates within groups.
10
+
11
+ The allocation decorator first obtains one complete K-slate from an injected
12
+ base policy. It then performs the minimum deterministic replacements needed
13
+ to satisfy declared group floors, choosing replacements and victims by the
14
+ same injected score. No current candidate outcome exists at this boundary.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import asyncio
20
+ import hashlib
21
+ import json
22
+ import re
23
+ from dataclasses import dataclass, field
24
+ from typing import ClassVar, Protocol, runtime_checkable
25
+
26
+ from agent_evolve.application.materialized_action_broker import (
27
+ MaterializedActionAllocationRequirement,
28
+ MaterializedActionDescriptor,
29
+ )
30
+ from agent_evolve.application.prequential_score_portfolio import (
31
+ MaterializedActionScoreBatch,
32
+ MaterializedActionScorePort,
33
+ )
34
+ from agent_evolve.application.residual_portfolio_evolution import (
35
+ MaterializedActionAllocationPolicyPort,
36
+ MaterializedActionProposalBatch,
37
+ ResidualPortfolioDecisionRequest,
38
+ )
39
+ from agent_evolve.domain.patch import require_sha256
40
+ from agent_evolve.domain.typed_json import (
41
+ FrozenJsonObject,
42
+ freeze_json,
43
+ thaw_json,
44
+ typed_json_sha256,
45
+ )
46
+
47
+ SOURCE_EXPOSURE_ALLOCATION_POLICY_ID = (
48
+ "minimum_source_exposure_allocator"
49
+ )
50
+ SOURCE_EXPOSURE_ALLOCATION_POLICY_VERSION = 1
51
+ MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION = 1
52
+ EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_ID = (
53
+ "explicit_expert_source_group_projection"
54
+ )
55
+ EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION = 1
56
+
57
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
58
+ _ASSIGNMENT_BATCH_DOMAIN = (
59
+ b"agent-evolve:materialized-source-group-assignment-batch:v1\x00"
60
+ )
61
+ _PROJECTION_DEFINITION_DOMAIN = (
62
+ b"agent-evolve:explicit-expert-source-group-projection:v1\x00"
63
+ )
64
+ _POLICY_DEFINITION_DOMAIN = (
65
+ b"agent-evolve:minimum-source-exposure-allocation:v1\x00"
66
+ )
67
+ _SLATE_FEASIBILITY_DEFINITION_DOMAIN = (
68
+ b"agent-evolve:minimum-expert-source-exposure-slate-feasibility:v1\x00"
69
+ )
70
+
71
+
72
+ def _canonical_json(value: object) -> bytes:
73
+ return json.dumps(
74
+ value,
75
+ allow_nan=False,
76
+ ensure_ascii=True,
77
+ separators=(",", ":"),
78
+ sort_keys=True,
79
+ ).encode("ascii", errors="strict")
80
+
81
+
82
+ def _hash(domain: bytes, value: object) -> str:
83
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
84
+
85
+
86
+ def _require_token(value: str, *, name: str) -> None:
87
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
88
+ raise ValueError(f"{name} must use the closed token grammar")
89
+
90
+
91
+ def _policy_identity(
92
+ value: MaterializedActionAllocationPolicyPort,
93
+ ) -> tuple[str, int, str]:
94
+ if not isinstance(value, MaterializedActionAllocationPolicyPort):
95
+ raise TypeError("base policy must implement its application port")
96
+ identity = (
97
+ getattr(value, "policy_id", None),
98
+ getattr(value, "policy_version", None),
99
+ getattr(value, "definition_sha256", None),
100
+ )
101
+ _require_token(identity[0], name="base policy_id")
102
+ if type(identity[1]) is not int or identity[1] <= 0:
103
+ raise ValueError("base policy_version must be positive")
104
+ require_sha256(identity[2], "base policy definition_sha256")
105
+ return identity # type: ignore[return-value]
106
+
107
+
108
+ def _scorer_identity(
109
+ value: MaterializedActionScorePort,
110
+ ) -> tuple[str, int, str]:
111
+ if not isinstance(value, MaterializedActionScorePort):
112
+ raise TypeError("priority scorer must implement its application port")
113
+ identity = (
114
+ getattr(value, "scorer_id", None),
115
+ getattr(value, "scorer_version", None),
116
+ getattr(value, "definition_sha256", None),
117
+ )
118
+ _require_token(identity[0], name="priority scorer_id")
119
+ if type(identity[1]) is not int or identity[1] <= 0:
120
+ raise ValueError("priority scorer_version must be positive")
121
+ require_sha256(identity[2], "priority scorer definition_sha256")
122
+ return identity # type: ignore[return-value]
123
+
124
+
125
+ @dataclass(frozen=True, slots=True)
126
+ class MaterializedActionSourceGroup:
127
+ action_sha256: str
128
+ group_id: str
129
+
130
+ def __post_init__(self) -> None:
131
+ require_sha256(self.action_sha256, "action_sha256")
132
+ _require_token(self.group_id, name="group_id")
133
+
134
+ def to_record(self) -> dict[str, object]:
135
+ self.__post_init__()
136
+ return {
137
+ "action_sha256": self.action_sha256,
138
+ "group_id": self.group_id,
139
+ }
140
+
141
+
142
+ @dataclass(frozen=True, slots=True)
143
+ class MaterializedActionSourceGroupBatch:
144
+ projection_id: str
145
+ projection_version: int
146
+ projection_definition_sha256: str
147
+ residual_request_sha256: str
148
+ proposal_sha256s: tuple[str, ...]
149
+ assignments: tuple[MaterializedActionSourceGroup, ...]
150
+ candidate_outcomes_observed: bool
151
+ evidence: FrozenJsonObject
152
+ batch_sha256: str = field(init=False)
153
+
154
+ def __post_init__(self) -> None:
155
+ _require_token(self.projection_id, name="projection_id")
156
+ if type(self.projection_version) is not int or self.projection_version <= 0:
157
+ raise ValueError("projection_version must be positive")
158
+ require_sha256(
159
+ self.projection_definition_sha256,
160
+ "projection_definition_sha256",
161
+ )
162
+ require_sha256(
163
+ self.residual_request_sha256,
164
+ "residual_request_sha256",
165
+ )
166
+ if (
167
+ type(self.proposal_sha256s) is not tuple
168
+ or not self.proposal_sha256s
169
+ or self.proposal_sha256s
170
+ != tuple(sorted(set(self.proposal_sha256s)))
171
+ ):
172
+ raise ValueError("proposal_sha256s must be non-empty and canonical")
173
+ for value in self.proposal_sha256s:
174
+ require_sha256(value, "proposal_sha256")
175
+ if type(self.assignments) is not tuple or not self.assignments:
176
+ raise ValueError("assignments must be a non-empty exact tuple")
177
+ for value in self.assignments:
178
+ if type(value) is not MaterializedActionSourceGroup:
179
+ raise TypeError("assignments must contain exact values")
180
+ value.__post_init__()
181
+ action_ids = tuple(value.action_sha256 for value in self.assignments)
182
+ if action_ids != tuple(sorted(set(action_ids))):
183
+ raise ValueError("assignments must be action-canonical")
184
+ if type(self.candidate_outcomes_observed) is not bool:
185
+ raise TypeError("candidate_outcomes_observed must be exact")
186
+ if self.candidate_outcomes_observed:
187
+ raise ValueError("source groups cannot observe current outcomes")
188
+ if (
189
+ type(self.evidence) is not FrozenJsonObject
190
+ or freeze_json(self.evidence) is not self.evidence
191
+ ):
192
+ raise TypeError("source-group evidence must be frozen")
193
+ object.__setattr__(
194
+ self,
195
+ "batch_sha256",
196
+ _hash(
197
+ _ASSIGNMENT_BATCH_DOMAIN,
198
+ {
199
+ "schema_version": 1,
200
+ "projection": {
201
+ "projection_id": self.projection_id,
202
+ "projection_version": self.projection_version,
203
+ "definition_sha256": (
204
+ self.projection_definition_sha256
205
+ ),
206
+ },
207
+ "residual_request_sha256": (
208
+ self.residual_request_sha256
209
+ ),
210
+ "proposal_sha256s": list(self.proposal_sha256s),
211
+ "assignments": [
212
+ value.to_record() for value in self.assignments
213
+ ],
214
+ "candidate_outcomes_observed": False,
215
+ "evidence_sha256": typed_json_sha256(self.evidence),
216
+ },
217
+ ),
218
+ )
219
+
220
+ def to_record(self, *, include_evidence: bool = False) -> dict[str, object]:
221
+ self.__post_init__()
222
+ record = {
223
+ "schema_version": 1,
224
+ "projection": {
225
+ "projection_id": self.projection_id,
226
+ "projection_version": self.projection_version,
227
+ "definition_sha256": self.projection_definition_sha256,
228
+ },
229
+ "residual_request_sha256": self.residual_request_sha256,
230
+ "proposal_sha256s": list(self.proposal_sha256s),
231
+ "assignments": [
232
+ value.to_record() for value in self.assignments
233
+ ],
234
+ "candidate_outcomes_observed": False,
235
+ "evidence_sha256": typed_json_sha256(self.evidence),
236
+ "batch_sha256": self.batch_sha256,
237
+ }
238
+ if include_evidence:
239
+ record["evidence"] = thaw_json(self.evidence)
240
+ return record
241
+
242
+
243
+ @runtime_checkable
244
+ class MaterializedActionSourceGroupProjectionPort(Protocol):
245
+ projection_id: str
246
+ projection_version: int
247
+ definition_sha256: str
248
+
249
+ async def project(
250
+ self,
251
+ request: ResidualPortfolioDecisionRequest,
252
+ proposals: tuple[MaterializedActionProposalBatch, ...],
253
+ ) -> MaterializedActionSourceGroupBatch: ...
254
+
255
+
256
+ def _projection_identity(
257
+ value: MaterializedActionSourceGroupProjectionPort,
258
+ ) -> tuple[str, int, str]:
259
+ if not isinstance(value, MaterializedActionSourceGroupProjectionPort):
260
+ raise TypeError("source projection must implement its application port")
261
+ identity = (
262
+ getattr(value, "projection_id", None),
263
+ getattr(value, "projection_version", None),
264
+ getattr(value, "definition_sha256", None),
265
+ )
266
+ _require_token(identity[0], name="source projection_id")
267
+ if type(identity[1]) is not int or identity[1] <= 0:
268
+ raise ValueError("source projection_version must be positive")
269
+ require_sha256(identity[2], "source projection definition_sha256")
270
+ return identity # type: ignore[return-value]
271
+
272
+
273
+ @dataclass(frozen=True, slots=True)
274
+ class ExplicitExpertSourceGroupProjection:
275
+ """Map opaque expert identities to opaque source groups."""
276
+
277
+ expert_group_bindings: tuple[tuple[str, str], ...]
278
+ projection_id: str = EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_ID
279
+ projection_version: int = (
280
+ EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION
281
+ )
282
+ definition_sha256: str = field(init=False)
283
+
284
+ def __post_init__(self) -> None:
285
+ if (
286
+ type(self.expert_group_bindings) is not tuple
287
+ or not self.expert_group_bindings
288
+ or self.expert_group_bindings
289
+ != tuple(sorted(self.expert_group_bindings))
290
+ ):
291
+ raise ValueError(
292
+ "expert_group_bindings must be non-empty and canonical"
293
+ )
294
+ expert_ids: list[str] = []
295
+ for expert_id, group_id in self.expert_group_bindings:
296
+ _require_token(expert_id, name="expert_id")
297
+ _require_token(group_id, name="group_id")
298
+ expert_ids.append(expert_id)
299
+ if len(expert_ids) != len(set(expert_ids)):
300
+ raise ValueError("expert_group_bindings repeat an expert")
301
+ _require_token(self.projection_id, name="projection_id")
302
+ if (
303
+ self.projection_version
304
+ != EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION
305
+ ):
306
+ raise ValueError("projection_version is immutable")
307
+ object.__setattr__(
308
+ self,
309
+ "definition_sha256",
310
+ _hash(
311
+ _PROJECTION_DEFINITION_DOMAIN,
312
+ {
313
+ "schema_version": 1,
314
+ "projection_id": self.projection_id,
315
+ "projection_version": self.projection_version,
316
+ "expert_group_bindings": [
317
+ {
318
+ "expert_id": expert_id,
319
+ "group_id": group_id,
320
+ }
321
+ for expert_id, group_id
322
+ in self.expert_group_bindings
323
+ ],
324
+ "candidate_outcomes_observed": False,
325
+ "workload_model_provider_branches": False,
326
+ },
327
+ ),
328
+ )
329
+
330
+ async def project(
331
+ self,
332
+ request: ResidualPortfolioDecisionRequest,
333
+ proposals: tuple[MaterializedActionProposalBatch, ...],
334
+ ) -> MaterializedActionSourceGroupBatch:
335
+ self.__post_init__()
336
+ if type(request) is not ResidualPortfolioDecisionRequest:
337
+ raise TypeError("request must be exact")
338
+ request.__post_init__()
339
+ if type(proposals) is not tuple or not proposals:
340
+ raise ValueError("proposals must be a non-empty exact tuple")
341
+ group_by_expert = dict(self.expert_group_bindings)
342
+ assignments: list[MaterializedActionSourceGroup] = []
343
+ proposal_sha256s: list[str] = []
344
+ for proposal in proposals:
345
+ if type(proposal) is not MaterializedActionProposalBatch:
346
+ raise TypeError("proposals must contain exact batches")
347
+ proposal.__post_init__()
348
+ proposal.require_request(request)
349
+ group_id = group_by_expert.get(proposal.expert_id)
350
+ if group_id is None:
351
+ raise ValueError("proposal expert has no source-group binding")
352
+ proposal_sha256s.append(proposal.proposal_sha256)
353
+ assignments.extend(
354
+ MaterializedActionSourceGroup(
355
+ action_sha256=action.action_sha256,
356
+ group_id=group_id,
357
+ )
358
+ for action in proposal.actions
359
+ )
360
+ if set(group_by_expert) != {
361
+ value.expert_id for value in proposals
362
+ }:
363
+ raise ValueError("source-group bindings name absent experts")
364
+ return MaterializedActionSourceGroupBatch(
365
+ projection_id=self.projection_id,
366
+ projection_version=self.projection_version,
367
+ projection_definition_sha256=self.definition_sha256,
368
+ residual_request_sha256=request.request_sha256,
369
+ proposal_sha256s=tuple(sorted(proposal_sha256s)),
370
+ assignments=tuple(
371
+ sorted(assignments, key=lambda value: value.action_sha256)
372
+ ),
373
+ candidate_outcomes_observed=False,
374
+ evidence=freeze_json(
375
+ {
376
+ "expert_count": len(group_by_expert),
377
+ "group_ids": sorted(set(group_by_expert.values())),
378
+ "mapping_is_explicit": True,
379
+ "candidate_outcomes_observed": False,
380
+ "workload_model_provider_branches": False,
381
+ }
382
+ ),
383
+ )
384
+
385
+
386
+ @dataclass(frozen=True, slots=True)
387
+ class MinimumExpertSourceExposureSlateFeasibility:
388
+ """Require phenotype uniqueness and opaque expert-group exposure floors."""
389
+
390
+ expert_group_bindings: tuple[tuple[str, str], ...]
391
+ minimum_exposures: tuple[tuple[str, int], ...]
392
+ feasibility_version: int = (
393
+ MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION
394
+ )
395
+ definition_sha256: str = field(init=False)
396
+
397
+ def __post_init__(self) -> None:
398
+ projection = ExplicitExpertSourceGroupProjection(
399
+ expert_group_bindings=self.expert_group_bindings,
400
+ )
401
+ if (
402
+ type(self.minimum_exposures) is not tuple
403
+ or not self.minimum_exposures
404
+ or self.minimum_exposures
405
+ != tuple(sorted(self.minimum_exposures))
406
+ ):
407
+ raise ValueError(
408
+ "minimum_exposures must be non-empty and canonical"
409
+ )
410
+ group_ids: list[str] = []
411
+ for group_id, minimum in self.minimum_exposures:
412
+ _require_token(group_id, name="minimum exposure group_id")
413
+ if type(minimum) is not int or minimum <= 0:
414
+ raise ValueError("minimum exposures must be positive")
415
+ group_ids.append(group_id)
416
+ if len(group_ids) != len(set(group_ids)):
417
+ raise ValueError("minimum_exposures repeat a group")
418
+ available_groups = {
419
+ group_id for _expert_id, group_id
420
+ in self.expert_group_bindings
421
+ }
422
+ if not set(group_ids) <= available_groups:
423
+ raise ValueError("minimum exposure group has no expert binding")
424
+ if (
425
+ self.feasibility_version
426
+ != MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION
427
+ ):
428
+ raise ValueError("feasibility_version is immutable")
429
+ object.__setattr__(
430
+ self,
431
+ "definition_sha256",
432
+ _hash(
433
+ _SLATE_FEASIBILITY_DEFINITION_DOMAIN,
434
+ {
435
+ "schema_version": 1,
436
+ "feasibility_version": self.feasibility_version,
437
+ "source_projection_definition_sha256": (
438
+ projection.definition_sha256
439
+ ),
440
+ "expert_group_bindings": [
441
+ {
442
+ "expert_id": expert_id,
443
+ "group_id": group_id,
444
+ }
445
+ for expert_id, group_id
446
+ in self.expert_group_bindings
447
+ ],
448
+ "minimum_exposures": [
449
+ {"group_id": group_id, "minimum": minimum}
450
+ for group_id, minimum in self.minimum_exposures
451
+ ],
452
+ "phenotype_unique": True,
453
+ "workload_model_provider_branches": False,
454
+ },
455
+ ),
456
+ )
457
+
458
+ def permits(
459
+ self,
460
+ actions: tuple[MaterializedActionDescriptor, ...],
461
+ ) -> bool:
462
+ self.__post_init__()
463
+ if type(actions) is not tuple or not actions:
464
+ return False
465
+ if any(
466
+ type(value) is not MaterializedActionDescriptor
467
+ for value in actions
468
+ ):
469
+ raise TypeError("slate contains a foreign action")
470
+ for value in actions:
471
+ value.__post_init__()
472
+ phenotypes = tuple(
473
+ value.phenotype_identity_sha256 for value in actions
474
+ )
475
+ if len(phenotypes) != len(set(phenotypes)):
476
+ return False
477
+ group_by_expert = dict(self.expert_group_bindings)
478
+ groups = []
479
+ for value in actions:
480
+ group_id = group_by_expert.get(value.expert_id)
481
+ if group_id is None:
482
+ return False
483
+ groups.append(group_id)
484
+ return all(
485
+ groups.count(group_id) >= minimum
486
+ for group_id, minimum in self.minimum_exposures
487
+ )
488
+
489
+
490
+ def _selection_trace(
491
+ requirement: MaterializedActionAllocationRequirement,
492
+ ) -> tuple[dict[str, object], ...]:
493
+ evidence = thaw_json(requirement.evidence)
494
+ if type(evidence) is not dict:
495
+ raise TypeError("base allocation evidence must be an object")
496
+ raw = evidence.get("selection_trace")
497
+ if type(raw) is not list:
498
+ raise ValueError("base allocation evidence lacks selection_trace")
499
+ rows: list[dict[str, object]] = []
500
+ seen: set[str] = set()
501
+ for fallback, value in enumerate(raw, start=1):
502
+ if type(value) is not dict:
503
+ raise TypeError("base selection rows must be objects")
504
+ row = dict(value)
505
+ action_sha256 = row.get("action_sha256")
506
+ if type(action_sha256) is not str:
507
+ raise TypeError("base selection row lacks action_sha256")
508
+ require_sha256(action_sha256, "base action_sha256")
509
+ if action_sha256 in seen:
510
+ raise ValueError("base selection trace repeats an action")
511
+ seen.add(action_sha256)
512
+ ordinal = row.get("ordinal", fallback)
513
+ if type(ordinal) is not int or ordinal <= 0:
514
+ raise ValueError("base selection ordinal must be positive")
515
+ row["ordinal"] = ordinal
516
+ rows.append(row)
517
+ if seen != set(requirement.required_action_sha256s):
518
+ raise ValueError("base selection trace differs from required actions")
519
+ return tuple(
520
+ sorted(
521
+ rows,
522
+ key=lambda value: (
523
+ int(value["ordinal"]),
524
+ str(value["action_sha256"]),
525
+ ),
526
+ )
527
+ )
528
+
529
+
530
+ @dataclass(frozen=True, slots=True)
531
+ class MinimumSourceExposureAllocationPolicy:
532
+ """Decorate a complete base slate with score-ranked source floors."""
533
+
534
+ base_policy: MaterializedActionAllocationPolicyPort = field(
535
+ repr=False,
536
+ compare=False,
537
+ )
538
+ priority_scorer: MaterializedActionScorePort = field(
539
+ repr=False,
540
+ compare=False,
541
+ )
542
+ source_projection: MaterializedActionSourceGroupProjectionPort = field(
543
+ repr=False,
544
+ compare=False,
545
+ )
546
+ minimum_exposures: tuple[tuple[str, int], ...]
547
+ policy_id: ClassVar[str] = SOURCE_EXPOSURE_ALLOCATION_POLICY_ID
548
+ policy_version: ClassVar[int] = (
549
+ SOURCE_EXPOSURE_ALLOCATION_POLICY_VERSION
550
+ )
551
+ definition_sha256: str = field(init=False)
552
+
553
+ def __post_init__(self) -> None:
554
+ base_identity = _policy_identity(self.base_policy)
555
+ scorer_identity = _scorer_identity(self.priority_scorer)
556
+ projection_identity = _projection_identity(
557
+ self.source_projection
558
+ )
559
+ if (
560
+ type(self.minimum_exposures) is not tuple
561
+ or not self.minimum_exposures
562
+ or self.minimum_exposures
563
+ != tuple(sorted(self.minimum_exposures))
564
+ ):
565
+ raise ValueError(
566
+ "minimum_exposures must be non-empty and canonical"
567
+ )
568
+ group_ids: list[str] = []
569
+ for group_id, minimum in self.minimum_exposures:
570
+ _require_token(group_id, name="minimum exposure group_id")
571
+ if type(minimum) is not int or minimum <= 0:
572
+ raise ValueError("minimum exposures must be positive")
573
+ group_ids.append(group_id)
574
+ if len(group_ids) != len(set(group_ids)):
575
+ raise ValueError("minimum_exposures repeat a group")
576
+ object.__setattr__(
577
+ self,
578
+ "definition_sha256",
579
+ _hash(
580
+ _POLICY_DEFINITION_DOMAIN,
581
+ {
582
+ "schema_version": 1,
583
+ "policy_id": self.policy_id,
584
+ "policy_version": self.policy_version,
585
+ "base_policy": {
586
+ "policy_id": base_identity[0],
587
+ "policy_version": base_identity[1],
588
+ "definition_sha256": base_identity[2],
589
+ },
590
+ "priority_scorer": {
591
+ "scorer_id": scorer_identity[0],
592
+ "scorer_version": scorer_identity[1],
593
+ "definition_sha256": scorer_identity[2],
594
+ },
595
+ "source_projection": {
596
+ "projection_id": projection_identity[0],
597
+ "projection_version": projection_identity[1],
598
+ "definition_sha256": projection_identity[2],
599
+ },
600
+ "minimum_exposures": [
601
+ {"group_id": group_id, "minimum": minimum}
602
+ for group_id, minimum in self.minimum_exposures
603
+ ],
604
+ "repair": (
605
+ "minimum_score_ranked_replacements_of_lowest_score_"
606
+ "removable_members"
607
+ ),
608
+ "candidate_outcomes_observed": False,
609
+ "workload_model_provider_branches": False,
610
+ },
611
+ ),
612
+ )
613
+
614
+ async def require(
615
+ self,
616
+ request: ResidualPortfolioDecisionRequest,
617
+ proposals: tuple[MaterializedActionProposalBatch, ...],
618
+ ) -> MaterializedActionAllocationRequirement:
619
+ self.__post_init__()
620
+ if type(request) is not ResidualPortfolioDecisionRequest:
621
+ raise TypeError("request must be exact")
622
+ request.__post_init__()
623
+ if type(proposals) is not tuple or not proposals:
624
+ raise ValueError("proposals must be a non-empty exact tuple")
625
+ actions: list[MaterializedActionDescriptor] = []
626
+ for proposal in proposals:
627
+ if type(proposal) is not MaterializedActionProposalBatch:
628
+ raise TypeError("proposals must contain exact batches")
629
+ proposal.__post_init__()
630
+ proposal.require_request(request)
631
+ actions.extend(proposal.actions)
632
+ action_by_sha256 = {
633
+ value.action_sha256: value for value in actions
634
+ }
635
+ if len(action_by_sha256) != len(actions):
636
+ raise ValueError("proposal market repeats an action")
637
+ proposal_sha256s = tuple(
638
+ sorted(value.proposal_sha256 for value in proposals)
639
+ )
640
+ base, score_batch, group_batch = await asyncio.gather(
641
+ self.base_policy.require(request, proposals),
642
+ self.priority_scorer.score(request, proposals),
643
+ self.source_projection.project(request, proposals),
644
+ )
645
+ if type(base) is not MaterializedActionAllocationRequirement:
646
+ raise TypeError("base policy returned a foreign requirement")
647
+ base.__post_init__()
648
+ if type(score_batch) is not MaterializedActionScoreBatch:
649
+ raise TypeError("priority scorer returned a foreign batch")
650
+ score_batch.__post_init__()
651
+ if type(group_batch) is not MaterializedActionSourceGroupBatch:
652
+ raise TypeError("source projection returned a foreign batch")
653
+ group_batch.__post_init__()
654
+ if (
655
+ (
656
+ base.residual_request_sha256,
657
+ base.proposal_sha256s,
658
+ )
659
+ != (request.request_sha256, proposal_sha256s)
660
+ or (
661
+ score_batch.residual_request_sha256,
662
+ score_batch.proposal_sha256s,
663
+ )
664
+ != (request.request_sha256, proposal_sha256s)
665
+ or (
666
+ group_batch.residual_request_sha256,
667
+ group_batch.proposal_sha256s,
668
+ )
669
+ != (request.request_sha256, proposal_sha256s)
670
+ ):
671
+ raise ValueError("allocation inputs saw different sealed markets")
672
+ action_ids = tuple(sorted(action_by_sha256))
673
+ if (
674
+ tuple(value.action_sha256 for value in score_batch.scores)
675
+ != action_ids
676
+ or tuple(
677
+ value.action_sha256 for value in group_batch.assignments
678
+ )
679
+ != action_ids
680
+ ):
681
+ raise ValueError("score/group batches do not cover the market")
682
+ if len(base.required_action_sha256s) != request.evaluation_slots:
683
+ raise ValueError("source exposure requires a complete base K-slate")
684
+ if sum(value for _group, value in self.minimum_exposures) > (
685
+ request.evaluation_slots
686
+ ):
687
+ raise ValueError("minimum source exposures exceed K")
688
+
689
+ score_by_action = {
690
+ value.action_sha256: value.value
691
+ for value in score_batch.scores
692
+ }
693
+ group_by_action = {
694
+ value.action_sha256: value.group_id
695
+ for value in group_batch.assignments
696
+ }
697
+ minima = dict(self.minimum_exposures)
698
+ if not set(minima).issubset(set(group_by_action.values())):
699
+ raise ValueError("a required source group is absent")
700
+ base_trace = _selection_trace(base)
701
+ selected = [
702
+ action_by_sha256[str(value["action_sha256"])]
703
+ for value in base_trace
704
+ ]
705
+ if len(
706
+ {value.phenotype_identity_sha256 for value in selected}
707
+ ) != len(selected):
708
+ raise ValueError("base K-slate repeats a phenotype")
709
+ original_rows = {
710
+ str(value["action_sha256"]): dict(value)
711
+ for value in base_trace
712
+ }
713
+ inserted: set[str] = set()
714
+ replacements: list[dict[str, object]] = []
715
+
716
+ def group_counts() -> dict[str, int]:
717
+ result: dict[str, int] = {}
718
+ for action in selected:
719
+ group_id = group_by_action[action.action_sha256]
720
+ result[group_id] = result.get(group_id, 0) + 1
721
+ return result
722
+
723
+ rankings = tuple(
724
+ sorted(
725
+ actions,
726
+ key=lambda value: (
727
+ -score_by_action[value.action_sha256],
728
+ value.native_rank,
729
+ value.expert_id,
730
+ value.action_sha256,
731
+ ),
732
+ )
733
+ )
734
+ for group_id, minimum in self.minimum_exposures:
735
+ while group_counts().get(group_id, 0) < minimum:
736
+ counts = group_counts()
737
+ removable_indices = tuple(
738
+ index
739
+ for index, action in enumerate(selected)
740
+ if not action.reference_action
741
+ and counts[group_by_action[action.action_sha256]]
742
+ > minima.get(
743
+ group_by_action[action.action_sha256],
744
+ 0,
745
+ )
746
+ )
747
+ if not removable_indices:
748
+ raise ValueError(
749
+ "source floors leave no removable base member"
750
+ )
751
+ replacement_pair: tuple[
752
+ MaterializedActionDescriptor,
753
+ int,
754
+ ] | None = None
755
+ selected_ids = {
756
+ value.action_sha256 for value in selected
757
+ }
758
+ for candidate in rankings:
759
+ if (
760
+ candidate.action_sha256 in selected_ids
761
+ or group_by_action[candidate.action_sha256]
762
+ != group_id
763
+ ):
764
+ continue
765
+ eligible_victims = tuple(
766
+ index
767
+ for index in removable_indices
768
+ if all(
769
+ other_index == index
770
+ or other.phenotype_identity_sha256
771
+ != candidate.phenotype_identity_sha256
772
+ for other_index, other in enumerate(selected)
773
+ )
774
+ )
775
+ if not eligible_victims:
776
+ continue
777
+ victim_index = min(
778
+ eligible_victims,
779
+ key=lambda index: (
780
+ score_by_action[
781
+ selected[index].action_sha256
782
+ ],
783
+ -selected[index].native_rank,
784
+ selected[index].action_sha256,
785
+ ),
786
+ )
787
+ replacement_pair = (candidate, victim_index)
788
+ break
789
+ if replacement_pair is None:
790
+ raise ValueError(
791
+ "source floor lacks a unique-phenotype replacement"
792
+ )
793
+ candidate, victim_index = replacement_pair
794
+ victim = selected[victim_index]
795
+ selected[victim_index] = candidate
796
+ inserted.add(candidate.action_sha256)
797
+ replacements.append(
798
+ {
799
+ "ordinal": len(replacements) + 1,
800
+ "required_group_id": group_id,
801
+ "inserted_action_sha256": (
802
+ candidate.action_sha256
803
+ ),
804
+ "inserted_score_hex": score_by_action[
805
+ candidate.action_sha256
806
+ ].hex(),
807
+ "removed_action_sha256": victim.action_sha256,
808
+ "removed_group_id": group_by_action[
809
+ victim.action_sha256
810
+ ],
811
+ "removed_score_hex": score_by_action[
812
+ victim.action_sha256
813
+ ].hex(),
814
+ "candidate_outcomes_observed": False,
815
+ }
816
+ )
817
+
818
+ final_counts = group_counts()
819
+ if any(
820
+ final_counts.get(group_id, 0) < minimum
821
+ for group_id, minimum in self.minimum_exposures
822
+ ):
823
+ raise AssertionError("source exposure repair did not close")
824
+ if len(selected) != request.evaluation_slots or len(
825
+ {value.phenotype_identity_sha256 for value in selected}
826
+ ) != len(selected):
827
+ raise AssertionError("source exposure repair corrupted K")
828
+ selection_trace: list[dict[str, object]] = []
829
+ for ordinal, action in enumerate(selected, start=1):
830
+ if action.action_sha256 in inserted:
831
+ row: dict[str, object] = {
832
+ "ordinal": ordinal,
833
+ "allocation_kind": "source_exposure_floor",
834
+ "score_lane": score_batch.scorer_id,
835
+ "action_sha256": action.action_sha256,
836
+ "score_hex": score_by_action[
837
+ action.action_sha256
838
+ ].hex(),
839
+ "source_group_id": group_by_action[
840
+ action.action_sha256
841
+ ],
842
+ "candidate_outcomes_observed": False,
843
+ }
844
+ else:
845
+ row = dict(original_rows[action.action_sha256])
846
+ row["ordinal"] = ordinal
847
+ row["source_group_id"] = group_by_action[
848
+ action.action_sha256
849
+ ]
850
+ row["source_exposure_status"] = "base_retained"
851
+ selection_trace.append(row)
852
+ return MaterializedActionAllocationRequirement(
853
+ policy_id=self.policy_id,
854
+ policy_version=self.policy_version,
855
+ policy_definition_sha256=self.definition_sha256,
856
+ residual_request_sha256=request.request_sha256,
857
+ proposal_sha256s=proposal_sha256s,
858
+ required_action_sha256s=tuple(
859
+ sorted(value.action_sha256 for value in selected)
860
+ ),
861
+ candidate_outcomes_observed=False,
862
+ evidence=freeze_json(
863
+ {
864
+ "base_requirement_sha256": base.requirement_sha256,
865
+ "priority_score_batch_sha256": (
866
+ score_batch.batch_sha256
867
+ ),
868
+ "source_group_batch_sha256": group_batch.batch_sha256,
869
+ "minimum_exposures": [
870
+ {"group_id": group_id, "minimum": minimum}
871
+ for group_id, minimum in self.minimum_exposures
872
+ ],
873
+ "base_group_counts": {
874
+ group_id: sum(
875
+ group_by_action[action_sha256] == group_id
876
+ for action_sha256
877
+ in base.required_action_sha256s
878
+ )
879
+ for group_id in sorted(set(group_by_action.values()))
880
+ },
881
+ "final_group_counts": {
882
+ group_id: final_counts[group_id]
883
+ for group_id in sorted(final_counts)
884
+ },
885
+ "replacements": replacements,
886
+ "selection_trace": selection_trace,
887
+ "candidate_outcomes_observed": False,
888
+ "workload_model_provider_branches": False,
889
+ }
890
+ ),
891
+ )
892
+
893
+
894
+ __all__ = [
895
+ "EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_ID",
896
+ "EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION",
897
+ "SOURCE_EXPOSURE_ALLOCATION_POLICY_ID",
898
+ "SOURCE_EXPOSURE_ALLOCATION_POLICY_VERSION",
899
+ "MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION",
900
+ "ExplicitExpertSourceGroupProjection",
901
+ "MaterializedActionSourceGroup",
902
+ "MaterializedActionSourceGroupBatch",
903
+ "MaterializedActionSourceGroupProjectionPort",
904
+ "MinimumSourceExposureAllocationPolicy",
905
+ "MinimumExpertSourceExposureSlateFeasibility",
906
+ ]