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,643 @@
1
+ """Typed, workload-neutral contracts for bounded portfolio memory use.
2
+
3
+ Showing a memory card to a selector and claiming that a particular action was
4
+ supported by that card are different events. A single provider call exposes
5
+ the whole response slate to every prompt-visible card, so an uncited member is
6
+ *not* a blinded control. It is nevertheless useful to reserve uncited
7
+ exploration slots and to require every explicit card attribution to name an
8
+ action that trusted application code declared compatible before the call.
9
+
10
+ This module records those narrower claims. It owns no prompt renderer,
11
+ provider, benchmark, evaluator, memory bank, or allocation policy.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import hashlib
17
+ import json
18
+ import re
19
+ from dataclasses import dataclass, field
20
+ from enum import Enum
21
+
22
+ from agent_evolve.domain.patch import require_sha256
23
+
24
+
25
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
26
+ _OPTION_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,255}$")
27
+ _CONTRACT_DOMAIN = b"agent-evolve:bounded-portfolio-memory-dose-contract:v1\x00"
28
+ _MEMBER_DOMAIN = b"agent-evolve:portfolio-memory-dose-member:v1\x00"
29
+ _ASSESSMENT_DOMAIN = b"agent-evolve:portfolio-memory-dose-assessment:v1\x00"
30
+
31
+ BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_ID = "bounded_relevance_aware_memory_dose"
32
+ BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_VERSION = 1
33
+ BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_DEFINITION_SHA256 = hashlib.sha256(
34
+ b"agent-evolve:bounded-relevance-aware-memory-dose:v1;"
35
+ b"prompt-wide-exposure-not-blinding;exact-card-coverage;"
36
+ b"bounded-supported-member-count;bounded-cards-per-member;"
37
+ b"precommitted-card-option-compatibility;proposal-evaluation-join"
38
+ ).hexdigest()
39
+
40
+
41
+ def _canonical_json(value: object) -> bytes:
42
+ return json.dumps(
43
+ value,
44
+ allow_nan=False,
45
+ ensure_ascii=True,
46
+ separators=(",", ":"),
47
+ sort_keys=True,
48
+ ).encode("ascii", errors="strict")
49
+
50
+
51
+ def _hash(domain: bytes, value: object) -> str:
52
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
53
+
54
+
55
+ def _require_token(value: str, *, name: str) -> None:
56
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
57
+ raise ValueError(f"{name} must use the closed token grammar")
58
+
59
+
60
+ def _require_option_id(value: str, *, name: str) -> None:
61
+ if type(value) is not str or _OPTION_ID.fullmatch(value) is None:
62
+ raise ValueError(f"{name} must use the closed option grammar")
63
+
64
+
65
+ def _canonical_card_keys(values: tuple[str, ...], *, name: str) -> None:
66
+ if type(values) is not tuple or any(type(value) is not str for value in values):
67
+ raise TypeError(f"{name} must be an exact tuple of card keys")
68
+ for value in values:
69
+ _require_token(value, name=name)
70
+ if values != tuple(sorted(set(values))):
71
+ raise ValueError(f"{name} must be unique and canonical")
72
+
73
+
74
+ class PortfolioMemoryDoseStage(str, Enum):
75
+ """The two outcome-blind boundaries at which dose is checked."""
76
+
77
+ PROPOSED_SLATE = "proposed_slate"
78
+ EVALUATED_PORTFOLIO = "evaluated_portfolio"
79
+
80
+
81
+ class PortfolioMemoryExposureScope(str, Enum):
82
+ """What the dose receipt can honestly say about prompt exposure."""
83
+
84
+ PROMPT_WIDE = "prompt_wide"
85
+
86
+
87
+ class PortfolioMemoryDoseViolation(str, Enum):
88
+ """Closed failure reasons suitable for schema repair and durable traces."""
89
+
90
+ FOREIGN_CARD_ATTRIBUTION = "foreign_card_attribution"
91
+ INCOMPATIBLE_CARD_ACTION = "incompatible_card_action"
92
+ TOO_MANY_CARDS_PER_MEMBER = "too_many_cards_per_member"
93
+ ASSIGNED_CARD_OMITTED = "assigned_card_omitted"
94
+ TOO_FEW_SUPPORTED_MEMBERS = "too_few_supported_members"
95
+ TOO_MANY_SUPPORTED_MEMBERS = "too_many_supported_members"
96
+ TOO_FEW_UNATTRIBUTED_MEMBERS = "too_few_unattributed_members"
97
+ EVALUATED_MEMBER_NOT_IN_PROPOSAL = "evaluated_member_not_in_proposal"
98
+
99
+
100
+ @dataclass(frozen=True, slots=True)
101
+ class PortfolioMemoryDoseCardSupport:
102
+ """Pre-call exact finite options to which one card may be attributed."""
103
+
104
+ card_key: str
105
+ card_content_sha256: str
106
+ finite_contract_identity_sha256: str
107
+ compatible_options: tuple[tuple[str, str], ...]
108
+ support_policy_id: str
109
+ support_policy_version: int
110
+ support_policy_definition_sha256: str
111
+
112
+ def __post_init__(self) -> None:
113
+ _require_token(self.card_key, name="card_key")
114
+ for name in (
115
+ "card_content_sha256",
116
+ "finite_contract_identity_sha256",
117
+ "support_policy_definition_sha256",
118
+ ):
119
+ require_sha256(getattr(self, name), name)
120
+ if type(self.compatible_options) is not tuple or not self.compatible_options:
121
+ raise ValueError("compatible_options must be a non-empty exact tuple")
122
+ option_ids: list[str] = []
123
+ option_identities: list[str] = []
124
+ for item in self.compatible_options:
125
+ if type(item) is not tuple or len(item) != 2:
126
+ raise TypeError("compatible_options must contain exact pairs")
127
+ option_id, option_identity_sha256 = item
128
+ _require_option_id(option_id, name="compatible option_id")
129
+ require_sha256(option_identity_sha256, "compatible option identity")
130
+ option_ids.append(option_id)
131
+ option_identities.append(option_identity_sha256)
132
+ if self.compatible_options != tuple(sorted(set(self.compatible_options))):
133
+ raise ValueError("compatible_options must be unique and canonical")
134
+ if len(set(option_ids)) != len(option_ids):
135
+ raise ValueError("compatible_options cannot repeat an option_id")
136
+ if len(set(option_identities)) != len(option_identities):
137
+ raise ValueError("compatible_options cannot repeat an option identity")
138
+ _require_token(self.support_policy_id, name="support_policy_id")
139
+ if type(self.support_policy_version) is not int or self.support_policy_version <= 0:
140
+ raise ValueError("support_policy_version must be positive")
141
+
142
+ def supports(self, option_id: str, option_identity_sha256: str) -> bool:
143
+ self.__post_init__()
144
+ return (option_id, option_identity_sha256) in self.compatible_options
145
+
146
+ def to_record(self) -> dict[str, object]:
147
+ self.__post_init__()
148
+ return {
149
+ "card_key": self.card_key,
150
+ "card_content_sha256": self.card_content_sha256,
151
+ "finite_contract_identity_sha256": self.finite_contract_identity_sha256,
152
+ "compatible_options": [
153
+ {
154
+ "option_id": option_id,
155
+ "option_identity_sha256": option_identity_sha256,
156
+ }
157
+ for option_id, option_identity_sha256 in self.compatible_options
158
+ ],
159
+ "support_policy": {
160
+ "policy_id": self.support_policy_id,
161
+ "policy_version": self.support_policy_version,
162
+ "definition_sha256": self.support_policy_definition_sha256,
163
+ },
164
+ }
165
+
166
+
167
+ @dataclass(frozen=True, slots=True)
168
+ class BoundedPortfolioMemoryDoseContract:
169
+ """Precommitted bounds for explicit card-supported proposal/evaluation slots.
170
+
171
+ ``supported`` always means an explicit, compatibility-checked attribution.
172
+ It never means that other members were hidden from the card. True card
173
+ blinding requires a separate prompt-redacted provider call.
174
+ """
175
+
176
+ card_supports: tuple[PortfolioMemoryDoseCardSupport, ...]
177
+ proposed_supported_member_bounds: tuple[int, int]
178
+ evaluated_supported_member_bounds: tuple[int, int]
179
+ minimum_unattributed_proposed_members: int
180
+ minimum_unattributed_evaluated_members: int
181
+ maximum_cards_per_member: int = 1
182
+ require_every_assigned_card: bool = True
183
+ exposure_scope: PortfolioMemoryExposureScope = (
184
+ PortfolioMemoryExposureScope.PROMPT_WIDE
185
+ )
186
+ policy_id: str = BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_ID
187
+ policy_version: int = BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_VERSION
188
+ policy_definition_sha256: str = (
189
+ BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_DEFINITION_SHA256
190
+ )
191
+ contract_sha256: str = field(init=False)
192
+
193
+ def __post_init__(self) -> None:
194
+ if (
195
+ type(self.card_supports) is not tuple
196
+ or not self.card_supports
197
+ or any(
198
+ type(value) is not PortfolioMemoryDoseCardSupport
199
+ for value in self.card_supports
200
+ )
201
+ ):
202
+ raise ValueError("card_supports must contain exact support values")
203
+ for value in self.card_supports:
204
+ PortfolioMemoryDoseCardSupport.__post_init__(value)
205
+ keys = tuple(value.card_key for value in self.card_supports)
206
+ if keys != tuple(sorted(set(keys))):
207
+ raise ValueError("card_supports must use unique canonical card order")
208
+ contract_ids = {
209
+ value.finite_contract_identity_sha256 for value in self.card_supports
210
+ }
211
+ if len(contract_ids) != 1:
212
+ raise ValueError("all card supports must bind one finite contract")
213
+ for name in (
214
+ "proposed_supported_member_bounds",
215
+ "evaluated_supported_member_bounds",
216
+ ):
217
+ bounds = getattr(self, name)
218
+ if (
219
+ type(bounds) is not tuple
220
+ or len(bounds) != 2
221
+ or any(type(value) is not int for value in bounds)
222
+ ):
223
+ raise TypeError(f"{name} must be an exact integer pair")
224
+ lower, upper = bounds
225
+ if lower < 0 or upper < lower:
226
+ raise ValueError(f"{name} must be ordered and non-negative")
227
+ if self.require_every_assigned_card and lower < len(keys):
228
+ raise ValueError(
229
+ f"{name} lower bound cannot cover every assigned card"
230
+ )
231
+ for name in (
232
+ "minimum_unattributed_proposed_members",
233
+ "minimum_unattributed_evaluated_members",
234
+ ):
235
+ value = getattr(self, name)
236
+ if type(value) is not int or value < 0:
237
+ raise ValueError(f"{name} must be a non-negative exact integer")
238
+ if type(self.maximum_cards_per_member) is not int or (
239
+ self.maximum_cards_per_member <= 0
240
+ ):
241
+ raise ValueError("maximum_cards_per_member must be positive")
242
+ if type(self.require_every_assigned_card) is not bool:
243
+ raise TypeError("require_every_assigned_card must be exact bool")
244
+ if self.exposure_scope is not PortfolioMemoryExposureScope.PROMPT_WIDE:
245
+ raise ValueError("bounded dose v1 records prompt-wide exposure only")
246
+ if (
247
+ self.policy_id != BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_ID
248
+ or self.policy_version != BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_VERSION
249
+ or self.policy_definition_sha256
250
+ != BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_DEFINITION_SHA256
251
+ ):
252
+ raise ValueError("unsupported bounded memory-dose policy")
253
+ object.__setattr__(
254
+ self,
255
+ "contract_sha256",
256
+ _hash(_CONTRACT_DOMAIN, self._unsigned_record()),
257
+ )
258
+
259
+ @property
260
+ def assigned_card_keys(self) -> tuple[str, ...]:
261
+ return tuple(value.card_key for value in self.card_supports)
262
+
263
+ @property
264
+ def finite_contract_identity_sha256(self) -> str:
265
+ return self.card_supports[0].finite_contract_identity_sha256
266
+
267
+ def support_for(self, card_key: str) -> PortfolioMemoryDoseCardSupport:
268
+ self.__post_init__()
269
+ for value in self.card_supports:
270
+ if value.card_key == card_key:
271
+ return value
272
+ raise ValueError("card_key is outside the bounded dose contract")
273
+
274
+ def bounds_for(self, stage: PortfolioMemoryDoseStage) -> tuple[int, int]:
275
+ if stage is PortfolioMemoryDoseStage.PROPOSED_SLATE:
276
+ return self.proposed_supported_member_bounds
277
+ if stage is PortfolioMemoryDoseStage.EVALUATED_PORTFOLIO:
278
+ return self.evaluated_supported_member_bounds
279
+ raise TypeError("stage must be exact PortfolioMemoryDoseStage")
280
+
281
+ def minimum_unattributed_for(self, stage: PortfolioMemoryDoseStage) -> int:
282
+ if stage is PortfolioMemoryDoseStage.PROPOSED_SLATE:
283
+ return self.minimum_unattributed_proposed_members
284
+ if stage is PortfolioMemoryDoseStage.EVALUATED_PORTFOLIO:
285
+ return self.minimum_unattributed_evaluated_members
286
+ raise TypeError("stage must be exact PortfolioMemoryDoseStage")
287
+
288
+ def _unsigned_record(self) -> dict[str, object]:
289
+ return {
290
+ "schema_version": 1,
291
+ "card_supports": [value.to_record() for value in self.card_supports],
292
+ "proposed_supported_member_bounds": list(
293
+ self.proposed_supported_member_bounds
294
+ ),
295
+ "evaluated_supported_member_bounds": list(
296
+ self.evaluated_supported_member_bounds
297
+ ),
298
+ "minimum_unattributed_proposed_members": (
299
+ self.minimum_unattributed_proposed_members
300
+ ),
301
+ "minimum_unattributed_evaluated_members": (
302
+ self.minimum_unattributed_evaluated_members
303
+ ),
304
+ "maximum_cards_per_member": self.maximum_cards_per_member,
305
+ "require_every_assigned_card": self.require_every_assigned_card,
306
+ "exposure_scope": self.exposure_scope.value,
307
+ "policy": {
308
+ "policy_id": self.policy_id,
309
+ "policy_version": self.policy_version,
310
+ "definition_sha256": self.policy_definition_sha256,
311
+ },
312
+ "unattributed_member_interpretation": (
313
+ "prompt_exposed_exploration_not_blinded_control"
314
+ ),
315
+ }
316
+
317
+ def to_record(self) -> dict[str, object]:
318
+ self.__post_init__()
319
+ return {**self._unsigned_record(), "contract_sha256": self.contract_sha256}
320
+
321
+
322
+ @dataclass(frozen=True, slots=True)
323
+ class PortfolioMemoryDoseMember:
324
+ """One proposed or evaluated finite option with explicit card attribution."""
325
+
326
+ rank: int
327
+ option_id: str
328
+ option_identity_sha256: str
329
+ supporting_card_keys: tuple[str, ...]
330
+
331
+ def __post_init__(self) -> None:
332
+ if type(self.rank) is not int or self.rank <= 0:
333
+ raise ValueError("rank must be positive")
334
+ _require_option_id(self.option_id, name="option_id")
335
+ require_sha256(self.option_identity_sha256, "option_identity_sha256")
336
+ _canonical_card_keys(self.supporting_card_keys, name="supporting_card_keys")
337
+
338
+ def _content_record(self) -> dict[str, object]:
339
+ return {
340
+ "option_id": self.option_id,
341
+ "option_identity_sha256": self.option_identity_sha256,
342
+ "supporting_card_keys": list(self.supporting_card_keys),
343
+ }
344
+
345
+ @property
346
+ def content_binding_sha256(self) -> str:
347
+ self.__post_init__()
348
+ return _hash(_MEMBER_DOMAIN, self._content_record())
349
+
350
+ def to_record(self) -> dict[str, object]:
351
+ self.__post_init__()
352
+ return {
353
+ "rank": self.rank,
354
+ **self._content_record(),
355
+ "content_binding_sha256": self.content_binding_sha256,
356
+ }
357
+
358
+
359
+ @dataclass(frozen=True, slots=True)
360
+ class PortfolioMemoryDoseAssessment:
361
+ """Content-free, replayable result of one proposal/evaluation dose check."""
362
+
363
+ contract_sha256: str
364
+ stage: PortfolioMemoryDoseStage
365
+ member_content_binding_sha256s: tuple[str, ...]
366
+ supported_member_ranks: tuple[int, ...]
367
+ unattributed_member_ranks: tuple[int, ...]
368
+ card_attribution_ranks: tuple[tuple[str, tuple[int, ...]], ...]
369
+ violations: tuple[PortfolioMemoryDoseViolation, ...]
370
+ proposal_assessment_sha256: str | None = None
371
+ assessment_sha256: str = field(init=False)
372
+
373
+ def __post_init__(self) -> None:
374
+ require_sha256(self.contract_sha256, "contract_sha256")
375
+ if type(self.stage) is not PortfolioMemoryDoseStage:
376
+ raise TypeError("stage must be exact PortfolioMemoryDoseStage")
377
+ if (
378
+ type(self.member_content_binding_sha256s) is not tuple
379
+ or not self.member_content_binding_sha256s
380
+ ):
381
+ raise ValueError("member bindings must be a non-empty exact tuple")
382
+ for value in self.member_content_binding_sha256s:
383
+ require_sha256(value, "member content binding")
384
+ ranks = tuple(range(1, len(self.member_content_binding_sha256s) + 1))
385
+ for name in ("supported_member_ranks", "unattributed_member_ranks"):
386
+ values = getattr(self, name)
387
+ if type(values) is not tuple or values != tuple(sorted(set(values))):
388
+ raise ValueError(f"{name} must be unique and canonical")
389
+ if any(type(value) is not int or value not in ranks for value in values):
390
+ raise ValueError(f"{name} contains an invalid rank")
391
+ if set(self.supported_member_ranks).intersection(
392
+ self.unattributed_member_ranks
393
+ ) or set(self.supported_member_ranks).union(
394
+ self.unattributed_member_ranks
395
+ ) != set(ranks):
396
+ raise ValueError("supported/unattributed ranks must partition members")
397
+ if type(self.card_attribution_ranks) is not tuple:
398
+ raise TypeError("card_attribution_ranks must be an exact tuple")
399
+ keys: list[str] = []
400
+ for item in self.card_attribution_ranks:
401
+ if type(item) is not tuple or len(item) != 2:
402
+ raise TypeError("card_attribution_ranks must contain exact pairs")
403
+ card_key, card_ranks = item
404
+ _require_token(card_key, name="card attribution key")
405
+ if (
406
+ type(card_ranks) is not tuple
407
+ or card_ranks != tuple(sorted(set(card_ranks)))
408
+ or any(value not in ranks for value in card_ranks)
409
+ ):
410
+ raise ValueError("card attribution ranks must be canonical")
411
+ keys.append(card_key)
412
+ if keys != sorted(set(keys)):
413
+ raise ValueError("card_attribution_ranks must use canonical card order")
414
+ if type(self.violations) is not tuple or any(
415
+ type(value) is not PortfolioMemoryDoseViolation for value in self.violations
416
+ ):
417
+ raise TypeError("violations must contain exact closed values")
418
+ if self.violations != tuple(sorted(set(self.violations), key=lambda x: x.value)):
419
+ raise ValueError("violations must be unique and canonical")
420
+ if self.stage is PortfolioMemoryDoseStage.PROPOSED_SLATE:
421
+ if self.proposal_assessment_sha256 is not None:
422
+ raise ValueError("proposal assessment cannot name an earlier proposal")
423
+ elif self.proposal_assessment_sha256 is None:
424
+ raise ValueError("evaluated assessment must join its proposal")
425
+ else:
426
+ require_sha256(
427
+ self.proposal_assessment_sha256,
428
+ "proposal_assessment_sha256",
429
+ )
430
+ object.__setattr__(
431
+ self,
432
+ "assessment_sha256",
433
+ _hash(_ASSESSMENT_DOMAIN, self._unsigned_record()),
434
+ )
435
+
436
+ @property
437
+ def passed(self) -> bool:
438
+ return not self.violations
439
+
440
+ def _unsigned_record(self) -> dict[str, object]:
441
+ return {
442
+ "schema_version": 1,
443
+ "contract_sha256": self.contract_sha256,
444
+ "stage": self.stage.value,
445
+ "member_content_binding_sha256s": list(
446
+ self.member_content_binding_sha256s
447
+ ),
448
+ "supported_member_ranks": list(self.supported_member_ranks),
449
+ "unattributed_member_ranks": list(self.unattributed_member_ranks),
450
+ "card_attribution_ranks": [
451
+ {"card_key": key, "member_ranks": list(ranks)}
452
+ for key, ranks in self.card_attribution_ranks
453
+ ],
454
+ "violations": [value.value for value in self.violations],
455
+ "proposal_assessment_sha256": self.proposal_assessment_sha256,
456
+ "exposure_scope": PortfolioMemoryExposureScope.PROMPT_WIDE.value,
457
+ "unattributed_members_are_blinded_controls": False,
458
+ }
459
+
460
+ def to_record(self) -> dict[str, object]:
461
+ self.__post_init__()
462
+ return {
463
+ **self._unsigned_record(),
464
+ "passed": self.passed,
465
+ "assessment_sha256": self.assessment_sha256,
466
+ }
467
+
468
+
469
+ class PortfolioMemoryDoseRejected(ValueError):
470
+ """A proposed or evaluated slate violated its sealed dose contract."""
471
+
472
+ def __init__(self, assessment: PortfolioMemoryDoseAssessment) -> None:
473
+ if type(assessment) is not PortfolioMemoryDoseAssessment or assessment.passed:
474
+ raise ValueError("rejection requires a failed exact assessment")
475
+ super().__init__("portfolio violates its bounded memory-dose contract")
476
+ self.assessment = assessment
477
+
478
+
479
+ def _assess(
480
+ contract: BoundedPortfolioMemoryDoseContract,
481
+ members: tuple[PortfolioMemoryDoseMember, ...],
482
+ *,
483
+ stage: PortfolioMemoryDoseStage,
484
+ proposal_assessment: PortfolioMemoryDoseAssessment | None,
485
+ ) -> PortfolioMemoryDoseAssessment:
486
+ if type(contract) is not BoundedPortfolioMemoryDoseContract:
487
+ raise TypeError("contract must be exact BoundedPortfolioMemoryDoseContract")
488
+ contract.__post_init__()
489
+ if (
490
+ type(members) is not tuple
491
+ or not members
492
+ or any(type(value) is not PortfolioMemoryDoseMember for value in members)
493
+ ):
494
+ raise ValueError("members must contain exact dose members")
495
+ for value in members:
496
+ PortfolioMemoryDoseMember.__post_init__(value)
497
+ if tuple(value.rank for value in members) != tuple(range(1, len(members) + 1)):
498
+ raise ValueError("members must use contiguous rank order")
499
+ if len({value.option_id for value in members}) != len(members):
500
+ raise ValueError("members cannot repeat an option_id")
501
+ if len({value.option_identity_sha256 for value in members}) != len(members):
502
+ raise ValueError("members cannot repeat an option identity")
503
+ if type(stage) is not PortfolioMemoryDoseStage:
504
+ raise TypeError("stage must be exact PortfolioMemoryDoseStage")
505
+ if stage is PortfolioMemoryDoseStage.PROPOSED_SLATE:
506
+ if proposal_assessment is not None:
507
+ raise ValueError("proposal stage cannot join an earlier proposal")
508
+ else:
509
+ if type(proposal_assessment) is not PortfolioMemoryDoseAssessment:
510
+ raise TypeError("evaluated stage requires an exact proposal assessment")
511
+ PortfolioMemoryDoseAssessment.__post_init__(proposal_assessment)
512
+ if (
513
+ proposal_assessment.stage is not PortfolioMemoryDoseStage.PROPOSED_SLATE
514
+ or proposal_assessment.contract_sha256 != contract.contract_sha256
515
+ or not proposal_assessment.passed
516
+ ):
517
+ raise ValueError("evaluated stage requires a passing matching proposal")
518
+
519
+ violations: set[PortfolioMemoryDoseViolation] = set()
520
+ assigned = set(contract.assigned_card_keys)
521
+ support_by_key = {value.card_key: value for value in contract.card_supports}
522
+ card_ranks = {key: [] for key in contract.assigned_card_keys}
523
+ supported_ranks: list[int] = []
524
+ unattributed_ranks: list[int] = []
525
+ proposal_bindings = (
526
+ set()
527
+ if proposal_assessment is None
528
+ else set(proposal_assessment.member_content_binding_sha256s)
529
+ )
530
+ for member in members:
531
+ keys = member.supporting_card_keys
532
+ if keys:
533
+ supported_ranks.append(member.rank)
534
+ else:
535
+ unattributed_ranks.append(member.rank)
536
+ if len(keys) > contract.maximum_cards_per_member:
537
+ violations.add(PortfolioMemoryDoseViolation.TOO_MANY_CARDS_PER_MEMBER)
538
+ for card_key in keys:
539
+ if card_key not in assigned:
540
+ violations.add(PortfolioMemoryDoseViolation.FOREIGN_CARD_ATTRIBUTION)
541
+ continue
542
+ card_ranks[card_key].append(member.rank)
543
+ if not support_by_key[card_key].supports(
544
+ member.option_id,
545
+ member.option_identity_sha256,
546
+ ):
547
+ violations.add(PortfolioMemoryDoseViolation.INCOMPATIBLE_CARD_ACTION)
548
+ if proposal_assessment is not None and (
549
+ member.content_binding_sha256 not in proposal_bindings
550
+ ):
551
+ violations.add(
552
+ PortfolioMemoryDoseViolation.EVALUATED_MEMBER_NOT_IN_PROPOSAL
553
+ )
554
+
555
+ if contract.require_every_assigned_card and any(
556
+ not card_ranks[key] for key in contract.assigned_card_keys
557
+ ):
558
+ violations.add(PortfolioMemoryDoseViolation.ASSIGNED_CARD_OMITTED)
559
+ lower, upper = contract.bounds_for(stage)
560
+ if len(supported_ranks) < lower:
561
+ violations.add(PortfolioMemoryDoseViolation.TOO_FEW_SUPPORTED_MEMBERS)
562
+ if len(supported_ranks) > upper:
563
+ violations.add(PortfolioMemoryDoseViolation.TOO_MANY_SUPPORTED_MEMBERS)
564
+ if len(unattributed_ranks) < contract.minimum_unattributed_for(stage):
565
+ violations.add(PortfolioMemoryDoseViolation.TOO_FEW_UNATTRIBUTED_MEMBERS)
566
+ return PortfolioMemoryDoseAssessment(
567
+ contract_sha256=contract.contract_sha256,
568
+ stage=stage,
569
+ member_content_binding_sha256s=tuple(
570
+ value.content_binding_sha256 for value in members
571
+ ),
572
+ supported_member_ranks=tuple(supported_ranks),
573
+ unattributed_member_ranks=tuple(unattributed_ranks),
574
+ card_attribution_ranks=tuple(
575
+ (key, tuple(card_ranks[key])) for key in contract.assigned_card_keys
576
+ ),
577
+ violations=tuple(sorted(violations, key=lambda value: value.value)),
578
+ proposal_assessment_sha256=(
579
+ None
580
+ if proposal_assessment is None
581
+ else proposal_assessment.assessment_sha256
582
+ ),
583
+ )
584
+
585
+
586
+ def assess_proposed_portfolio_memory_dose(
587
+ contract: BoundedPortfolioMemoryDoseContract,
588
+ members: tuple[PortfolioMemoryDoseMember, ...],
589
+ ) -> PortfolioMemoryDoseAssessment:
590
+ """Assess the complete provider-authored slate before allocation."""
591
+
592
+ return _assess(
593
+ contract,
594
+ members,
595
+ stage=PortfolioMemoryDoseStage.PROPOSED_SLATE,
596
+ proposal_assessment=None,
597
+ )
598
+
599
+
600
+ def assess_evaluated_portfolio_memory_dose(
601
+ contract: BoundedPortfolioMemoryDoseContract,
602
+ members: tuple[PortfolioMemoryDoseMember, ...],
603
+ *,
604
+ proposal_assessment: PortfolioMemoryDoseAssessment,
605
+ ) -> PortfolioMemoryDoseAssessment:
606
+ """Assess the allocated portfolio and join it to the sealed proposal."""
607
+
608
+ return _assess(
609
+ contract,
610
+ members,
611
+ stage=PortfolioMemoryDoseStage.EVALUATED_PORTFOLIO,
612
+ proposal_assessment=proposal_assessment,
613
+ )
614
+
615
+
616
+ def require_passing_portfolio_memory_dose(
617
+ assessment: PortfolioMemoryDoseAssessment,
618
+ ) -> None:
619
+ """Raise a typed rejection while retaining the complete failure receipt."""
620
+
621
+ if type(assessment) is not PortfolioMemoryDoseAssessment:
622
+ raise TypeError("assessment must be exact PortfolioMemoryDoseAssessment")
623
+ PortfolioMemoryDoseAssessment.__post_init__(assessment)
624
+ if not assessment.passed:
625
+ raise PortfolioMemoryDoseRejected(assessment)
626
+
627
+
628
+ __all__ = [
629
+ "BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_DEFINITION_SHA256",
630
+ "BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_ID",
631
+ "BOUNDED_PORTFOLIO_MEMORY_DOSE_POLICY_VERSION",
632
+ "BoundedPortfolioMemoryDoseContract",
633
+ "PortfolioMemoryDoseAssessment",
634
+ "PortfolioMemoryDoseCardSupport",
635
+ "PortfolioMemoryDoseMember",
636
+ "PortfolioMemoryDoseRejected",
637
+ "PortfolioMemoryDoseStage",
638
+ "PortfolioMemoryDoseViolation",
639
+ "PortfolioMemoryExposureScope",
640
+ "assess_evaluated_portfolio_memory_dose",
641
+ "assess_proposed_portfolio_memory_dose",
642
+ "require_passing_portfolio_memory_dose",
643
+ ]