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,167 @@
1
+ """Problem protocol, objective specification, and validation outcome."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from collections.abc import Mapping
7
+ from dataclasses import dataclass
8
+ from numbers import Real
9
+ from typing import Any, Dict, Literal, Optional, Protocol, Sequence, TypeVar, runtime_checkable
10
+
11
+ Goal = Literal["min", "max"]
12
+ ConfigT = TypeVar("ConfigT")
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class ObjectiveSpec:
17
+ """Specification for a single optimisation objective.
18
+
19
+ ``description`` is the objective's MEANING -- what the number measures,
20
+ its units, what a good value looks like ("spec-attainment reward, sum of
21
+ nine clipped terms, maximised at 0 = every spec met"). It is rendered
22
+ into every model-facing prompt: an optimizer asked to trade objectives
23
+ it cannot interpret is reasoning blindfolded, and the resulting failure
24
+ is unattributable (channel defect vs capability). Optional so existing
25
+ problems keep working; a problem that leaves it empty is telling the
26
+ model "the name is all you get".
27
+ """
28
+
29
+ name: str
30
+ goal: Goal
31
+ description: str = ""
32
+
33
+
34
+ class ProblemContractError(RuntimeError):
35
+ """The problem adapter violated its declared objective/evaluation contract."""
36
+
37
+
38
+ def validate_objective_specs(objectives: Sequence[ObjectiveSpec]) -> None:
39
+ """Validate objective declarations before any proposal or evaluation work."""
40
+ if not objectives:
41
+ raise ProblemContractError("Problem must define at least one objective")
42
+ names = [spec.name for spec in objectives]
43
+ if any(not isinstance(name, str) or not name.strip() for name in names):
44
+ raise ProblemContractError("Objective names must be non-empty strings")
45
+ duplicates = sorted({name for name in names if names.count(name) > 1})
46
+ if duplicates:
47
+ raise ProblemContractError(f"Duplicate objective name(s): {', '.join(duplicates)}")
48
+ invalid_goals = [f"{spec.name}={spec.goal!r}" for spec in objectives
49
+ if spec.goal not in ("min", "max")]
50
+ if invalid_goals:
51
+ raise ProblemContractError(
52
+ "Objective goals must be 'min' or 'max': " + ", ".join(invalid_goals)
53
+ )
54
+
55
+
56
+ def normalize_objective_values(
57
+ values: Any,
58
+ objectives: Sequence[ObjectiveSpec],
59
+ ) -> Dict[str, float]:
60
+ """Return a complete finite objective vector or raise ``ProblemContractError``.
61
+
62
+ Evaluators must return exactly the declared objectives. Diagnostics belong in
63
+ a separate adapter-level artifact/metadata channel; accepting undeclared keys
64
+ here would make misspelled objective names too easy to overlook.
65
+ """
66
+ validate_objective_specs(objectives)
67
+ if not isinstance(values, Mapping):
68
+ raise ProblemContractError(
69
+ f"Problem.evaluate() must return a mapping, got {type(values).__name__}"
70
+ )
71
+
72
+ expected = {spec.name for spec in objectives}
73
+ actual = set(values.keys())
74
+ missing = sorted(expected - actual)
75
+ extra = sorted(str(key) for key in actual - expected)
76
+ if missing or extra:
77
+ details = []
78
+ if missing:
79
+ details.append("missing: " + ", ".join(missing))
80
+ if extra:
81
+ details.append("undeclared: " + ", ".join(extra))
82
+ raise ProblemContractError("Invalid objective mapping (" + "; ".join(details) + ")")
83
+
84
+ normalized: Dict[str, float] = {}
85
+ for spec in objectives:
86
+ value = values[spec.name]
87
+ if isinstance(value, bool) or not isinstance(value, Real):
88
+ raise ProblemContractError(
89
+ f"Objective {spec.name!r} must be a real number, got {type(value).__name__}"
90
+ )
91
+ number = float(value)
92
+ if not math.isfinite(number):
93
+ raise ProblemContractError(
94
+ f"Objective {spec.name!r} must be finite, got {number!r}"
95
+ )
96
+ normalized[spec.name] = number
97
+ return normalized
98
+
99
+
100
+ @dataclass(frozen=True)
101
+ class ValidationOutcome:
102
+ """Structured result of a feasibility pre-check.
103
+
104
+ ``failure_phase`` lets a problem label *where* a candidate broke (e.g.
105
+ ``"structural" | "constraint" | "simulation"``) so the loop can feed richer
106
+ failure context to the LLM. ``message`` is forwarded verbatim to the model.
107
+ """
108
+
109
+ ok: bool
110
+ failure_phase: Optional[str] = None
111
+ message: Optional[str] = None
112
+
113
+
114
+ @runtime_checkable
115
+ class Problem(Protocol[ConfigT]):
116
+ """Minimal interface that every optimisation problem must satisfy.
117
+
118
+ Required
119
+ --------
120
+ objectives : Sequence[ObjectiveSpec]
121
+ The objectives to optimise (at least one).
122
+ evaluate(config) -> Dict[str, float]
123
+ Return exactly one finite numeric value for every declared objective.
124
+ Raise ``ValueError`` with a descriptive message only for invalid /
125
+ infeasible configurations -- the message is forwarded to the LLM as
126
+ feedback. Infrastructure and programming errors must use other exception
127
+ types and abort the run rather than becoming candidate feedback.
128
+
129
+ Optional (detected via ``hasattr`` at runtime)
130
+ ----------------------------------------------
131
+ validate_detailed(config) -> ValidationOutcome
132
+ Structured feasibility pre-check carrying a ``failure_phase`` label.
133
+ validate(config) -> bool
134
+ Legacy boolean pre-check. **Raise** ``ValueError("...")`` when invalid
135
+ (never return ``False`` silently). Wrapped into a ``ValidationOutcome``.
136
+ search_space_description() -> str
137
+ Human-readable description of the configuration format, valid ranges,
138
+ and constraints. Included verbatim in LLM prompts.
139
+ render_candidate(config) -> str
140
+ Compact one-line summary of a configuration, used in failure / Pareto
141
+ lists shown to the LLM. Defaults to pretty JSON when absent.
142
+ candidate_key(config) -> str
143
+ Canonical key used to de-duplicate proposed candidates across the run
144
+ (so identical configs are not re-evaluated). Defaults to sorted JSON.
145
+
146
+ Optional attribute:
147
+
148
+ directives
149
+ A ``Directives`` provider supplying prompt wording for this problem.
150
+ When absent, the backbone's generic ``DefaultDirectives`` is used.
151
+
152
+ Optional attributes (not part of the protocol check):
153
+
154
+ candidate_model : type[pydantic.BaseModel]
155
+ Schema for one candidate; its JSON schema is shown to the LLM.
156
+ constraints_description : str
157
+ Extra free-text constraints injected into prompts.
158
+ example_config : dict
159
+ A reference configuration the model can imitate.
160
+ config_schema : dict
161
+ A pseudo-JSON schema for the configuration.
162
+ """
163
+
164
+ @property
165
+ def objectives(self) -> Sequence[ObjectiveSpec]: ...
166
+
167
+ def evaluate(self, config: ConfigT) -> Dict[str, float]: ...
@@ -0,0 +1,323 @@
1
+ """Result containers and Pareto-front utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from dataclasses import dataclass, field
7
+ from numbers import Real
8
+ from typing import Any, Dict, Generic, List, Optional, Sequence, Tuple, TypeVar
9
+
10
+ from agent_evolve.core.problem import ObjectiveSpec, ProblemContractError
11
+ from agent_evolve.core.telemetry import RunTelemetry
12
+
13
+ ConfigT = TypeVar("ConfigT")
14
+
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class ProviderUsageSummary:
19
+ """What the run spent, so a caller can answer "what did this cost me".
20
+
21
+ Counted from the calls actually made, never declared. ``calls`` is zero for
22
+ an uninformed proposer, and that zero is measured: a run that made no model
23
+ call still reports the block rather than omitting it, because an absent
24
+ field cannot be told apart from an unrecorded one.
25
+ """
26
+
27
+ calls: int = 0
28
+ input_tokens: Optional[int] = None
29
+ output_tokens: Optional[int] = None
30
+ cost_usd: Optional[str] = None
31
+ model: Optional[str] = None
32
+ reported_by: Optional[str] = None
33
+
34
+ def __post_init__(self) -> None:
35
+ if type(self.calls) is not int or self.calls < 0:
36
+ raise ValueError("calls must be a non-negative integer")
37
+ figures = {
38
+ "input_tokens": self.input_tokens,
39
+ "output_tokens": self.output_tokens,
40
+ "cost_usd": self.cost_usd,
41
+ }
42
+ present = {name for name, value in figures.items() if value is not None}
43
+ if present and not self.reported_by:
44
+ # A figure with no reporter is a number nobody measured. Zero is the
45
+ # dangerous case: it reads as "nothing was spent" when it means "no
46
+ # one looked". Naming the reporter is what makes the difference
47
+ # unrepresentable rather than merely documented.
48
+ raise ValueError(
49
+ f"usage figures {sorted(present)} require reported_by naming "
50
+ "what measured them"
51
+ )
52
+ if self.reported_by is not None and not present:
53
+ raise ValueError(
54
+ "reported_by names a reporter that supplied no figure"
55
+ )
56
+
57
+ @property
58
+ def provider_free(self) -> bool:
59
+ """True only when calls were counted and none occurred."""
60
+
61
+ return self.calls == 0
62
+
63
+ @property
64
+ def cost_is_known(self) -> bool:
65
+ return self.cost_usd is not None
66
+
67
+
68
+ @dataclass(frozen=True)
69
+ class Candidate(Generic[ConfigT]):
70
+ """A single evaluated configuration."""
71
+
72
+ configuration: ConfigT
73
+ objectives: Dict[str, float]
74
+ metadata: Dict[str, Any] = field(default_factory=dict)
75
+
76
+
77
+ @dataclass(frozen=True)
78
+ class SearchResult(Generic[ConfigT]):
79
+ """Aggregated output of an optimisation run.
80
+
81
+ Attributes
82
+ ----------
83
+ objectives : objectives used during the run.
84
+ best : the single recommended candidate (minimax rank over the Pareto front;
85
+ see :func:`select_minimax_rank`).
86
+ pareto_front : non-dominated set.
87
+ all_candidates : every unique candidate processed across all generations,
88
+ including deterministic validation failures.
89
+ history : per-generation summary dicts.
90
+ best_per_generation : minimax-best candidate on the cumulative Pareto front
91
+ after each generation (same rule as ``best``); useful for progress.
92
+ evaluations : exact number of ``Problem.evaluate`` invocations, including
93
+ calls that raise candidate-level ``ValueError``. Deterministic pre-check
94
+ failures do not increment this evaluator-call budget.
95
+ telemetry : what each guidance mechanism did, plus the real/virtual
96
+ evaluation ledger. Populated by the genetic loop; ``None`` on paths
97
+ that have not adopted it yet.
98
+ """
99
+
100
+ objectives: Sequence[ObjectiveSpec]
101
+ best: Candidate[ConfigT]
102
+ pareto_front: List[Candidate[ConfigT]] = field(default_factory=list)
103
+ all_candidates: List[Candidate[ConfigT]] = field(default_factory=list)
104
+ history: List[Dict[str, Any]] = field(default_factory=list)
105
+ best_per_generation: List[Candidate[ConfigT]] = field(default_factory=list)
106
+ evaluations: int = 0
107
+ provider_usage: "ProviderUsageSummary | None" = None
108
+ telemetry: Optional[RunTelemetry] = None
109
+
110
+ def candidates_by_author(self) -> Dict[str, int]:
111
+ """How many candidates each authoring call produced.
112
+
113
+ Publish this beside any comparison that treats "the model proposed it"
114
+ as an arm. A count is checkable; a label is only asserted, and an arm
115
+ named for the component that produced it rather than the authority that
116
+ decided it is a mistake that survives review because the numbers still
117
+ look plausible.
118
+ """
119
+ counts: Dict[str, int] = {}
120
+ for candidate in self.all_candidates:
121
+ author = candidate.metadata.get("authored_by", "unrecorded")
122
+ counts[author] = counts.get(author, 0) + 1
123
+ return counts
124
+
125
+ def proposed_candidates(self) -> List[Candidate[ConfigT]]:
126
+ """Only what the proposer authored: the caller's own seeds excluded."""
127
+
128
+ return [
129
+ candidate
130
+ for candidate in self.all_candidates
131
+ if candidate.metadata.get("authored_by", "").startswith("proposer_")
132
+ ]
133
+
134
+
135
+ # ------------------------------------------------------------------
136
+ # Pareto dominance
137
+ # ------------------------------------------------------------------
138
+
139
+ def objective_value(values: Dict[str, float], name: str) -> float:
140
+ """Read one required finite objective without fabricating a default value."""
141
+ if name not in values:
142
+ raise ProblemContractError(f"Missing declared objective {name!r}")
143
+ value = values[name]
144
+ if isinstance(value, bool) or not isinstance(value, Real):
145
+ raise ProblemContractError(
146
+ f"Objective {name!r} must be a real number, got {type(value).__name__}"
147
+ )
148
+ number = float(value)
149
+ if not math.isfinite(number):
150
+ raise ProblemContractError(f"Objective {name!r} must be finite, got {number!r}")
151
+ return number
152
+
153
+ def dominates(
154
+ a: Dict[str, float],
155
+ b: Dict[str, float],
156
+ objectives: Sequence[ObjectiveSpec],
157
+ ) -> bool:
158
+ """Return *True* if objective vector *a* Pareto-dominates *b*."""
159
+ all_geq = True
160
+ any_better = False
161
+ for spec in objectives:
162
+ va = objective_value(a, spec.name)
163
+ vb = objective_value(b, spec.name)
164
+ if spec.goal == "max":
165
+ if va < vb:
166
+ all_geq = False
167
+ elif va > vb:
168
+ any_better = True
169
+ else:
170
+ if va > vb:
171
+ all_geq = False
172
+ elif va < vb:
173
+ any_better = True
174
+ return all_geq and any_better
175
+
176
+
177
+ def compute_pareto_front(
178
+ candidates: Sequence[Candidate[ConfigT]],
179
+ objectives: Sequence[ObjectiveSpec],
180
+ ) -> List[Candidate[ConfigT]]:
181
+ """Return the non-dominated subset of *candidates*.
182
+
183
+ Exact duplicates — same configuration identity and same measured
184
+ objectives — collapse to their first occurrence, so a configuration a
185
+ population re-visits across generations appears once on the front rather
186
+ than once per visit. A configuration re-evaluated to *different*
187
+ objectives is a genuinely different measurement and both rows remain;
188
+ dropping one silently would be the library's judgement, not the caller's.
189
+ """
190
+ if not candidates:
191
+ return []
192
+ from agent_evolve.contract import artifact_key
193
+
194
+ unique: List[Candidate[ConfigT]] = []
195
+ seen: set = set()
196
+ for c in candidates:
197
+ key = (artifact_key(c.configuration), tuple(sorted(c.objectives.items())))
198
+ if key in seen:
199
+ continue
200
+ seen.add(key)
201
+ unique.append(c)
202
+ front: List[Candidate[ConfigT]] = []
203
+ for i, c in enumerate(unique):
204
+ if not any(
205
+ dominates(other.objectives, c.objectives, objectives)
206
+ for j, other in enumerate(unique)
207
+ if j != i
208
+ ):
209
+ front.append(c)
210
+ return front
211
+
212
+
213
+ # ------------------------------------------------------------------
214
+ # Best-candidate selection
215
+ # ------------------------------------------------------------------
216
+
217
+ def select_best_candidate(
218
+ pareto: Sequence[Candidate[ConfigT]],
219
+ objectives: Sequence[ObjectiveSpec],
220
+ priority_order: Optional[List[str]] = None,
221
+ ) -> Optional[Candidate[ConfigT]]:
222
+ """Lexicographic selection from the Pareto front.
223
+
224
+ Default priority: maximise objectives first, then minimise objectives.
225
+ """
226
+ if not pareto:
227
+ return None
228
+ if priority_order is None:
229
+ max_objs = [s for s in objectives if s.goal == "max"]
230
+ min_objs = [s for s in objectives if s.goal == "min"]
231
+ priority_order = [s.name for s in max_objs] + [s.name for s in min_objs]
232
+ obj_map = {s.name: s for s in objectives}
233
+ unknown = [name for name in priority_order if name not in obj_map]
234
+ if unknown:
235
+ raise ProblemContractError(
236
+ "Unknown priority objective name(s): " + ", ".join(unknown)
237
+ )
238
+
239
+ def _key(c: Candidate[ConfigT]) -> Tuple[float, ...]:
240
+ parts: List[float] = []
241
+ for name in priority_order:
242
+ spec = obj_map[name]
243
+ val = objective_value(c.objectives, name)
244
+ parts.append(-val if spec.goal == "max" else val)
245
+ return tuple(parts)
246
+
247
+ return min(pareto, key=_key)
248
+
249
+
250
+ # ------------------------------------------------------------------
251
+ # Minimax-rank selection
252
+ # ------------------------------------------------------------------
253
+
254
+ def _rank_candidates(
255
+ candidates: Sequence[Candidate[ConfigT]],
256
+ objectives: Sequence[ObjectiveSpec],
257
+ ) -> List[List[int]]:
258
+ """``ranks[i][j]`` = 1-based **dense** rank of candidate *i* on objective *j* (1 = best).
259
+
260
+ Ties share the same rank; the next distinct value gets the next integer (no gaps
261
+ from skipped positions).
262
+ """
263
+ n = len(candidates)
264
+ ranks: List[List[int]] = [[0] * len(objectives) for _ in range(n)]
265
+ for j, spec in enumerate(objectives):
266
+ values = [objective_value(c.objectives, spec.name) for c in candidates]
267
+ reverse = spec.goal == "max"
268
+ order = sorted(range(n), key=lambda i: values[i], reverse=reverse)
269
+ dense = 1
270
+ for pos, idx in enumerate(order):
271
+ if pos > 0 and values[order[pos]] != values[order[pos - 1]]:
272
+ dense += 1
273
+ ranks[idx][j] = dense
274
+ return ranks
275
+
276
+
277
+ def select_minimax_rank(
278
+ candidates: Sequence[Candidate[ConfigT]],
279
+ objectives: Sequence[ObjectiveSpec],
280
+ ) -> Optional[Candidate[ConfigT]]:
281
+ """Best pick for multi-objective summaries: minimax over per-objective ranks.
282
+
283
+ For each candidate, compute **dense** rank on each objective (1 = best among
284
+ *candidates*). Take the **maximum** rank across objectives (bottleneck / worst
285
+ placement). Prefer the candidate(s) with the **smallest** bottleneck (minimax).
286
+
287
+ If several tie, pick the one with the **smallest sum of ranks** (more uniform
288
+ strength, not a spike on one metric).
289
+ """
290
+ if not candidates:
291
+ return None
292
+ if len(candidates) == 1:
293
+ return candidates[0]
294
+ ranks = _rank_candidates(candidates, objectives)
295
+ worst = [max(r) for r in ranks]
296
+ min_worst = min(worst)
297
+ tied = [i for i in range(len(candidates)) if worst[i] == min_worst]
298
+ if len(tied) == 1:
299
+ return candidates[tied[0]]
300
+ best_idx = min(tied, key=lambda i: sum(ranks[i]))
301
+ return candidates[best_idx]
302
+
303
+
304
+ def sort_by_minimax_rank(
305
+ candidates: Sequence[Candidate[ConfigT]],
306
+ objectives: Sequence[ObjectiveSpec],
307
+ ) -> List[Candidate[ConfigT]]:
308
+ """Order *candidates* by the same rule as :func:`select_minimax_rank`.
309
+
310
+ Primary key: smallest worst per-objective (dense) rank. Secondary: smallest
311
+ sum of per-objective ranks. The first element matches what
312
+ ``select_minimax_rank(candidates, objectives)`` returns when not ``None``.
313
+ """
314
+ if not candidates:
315
+ return []
316
+ if len(candidates) == 1:
317
+ return [candidates[0]]
318
+ ranks = _rank_candidates(candidates, objectives)
319
+ order = sorted(
320
+ range(len(candidates)),
321
+ key=lambda i: (max(ranks[i]), sum(ranks[i])),
322
+ )
323
+ return [candidates[i] for i in order]
@@ -0,0 +1,70 @@
1
+ """Performance statistics and failure sampling for the insight prompts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import random
6
+ from typing import Any, Dict, List, Optional, Sequence
7
+
8
+ from agent_evolve.core.formatting import (
9
+ CandidateResult,
10
+ candidate_to_result,
11
+ result_to_candidate,
12
+ )
13
+ from agent_evolve.core.problem import ObjectiveSpec
14
+ from agent_evolve.core.results import compute_pareto_front, objective_value, sort_by_minimax_rank
15
+
16
+
17
+ def compute_performance_stats(
18
+ valid_results: Sequence[CandidateResult],
19
+ objectives: Sequence[ObjectiveSpec],
20
+ ) -> Optional[Dict[str, Any]]:
21
+ """Compute best/worst per objective and the top Pareto candidates."""
22
+ if not valid_results:
23
+ return None
24
+
25
+ stats: Dict[str, Any] = {}
26
+
27
+ for spec in objectives:
28
+ key = spec.name
29
+ if spec.goal == "max":
30
+ best = max(valid_results, key=lambda r: objective_value(r.objectives, key))
31
+ worst = min(valid_results, key=lambda r: objective_value(r.objectives, key))
32
+ else:
33
+ best = min(valid_results, key=lambda r: objective_value(r.objectives, key))
34
+ worst = max(valid_results, key=lambda r: objective_value(r.objectives, key))
35
+ stats[f"best_{key}"] = best
36
+ stats[f"worst_{key}"] = worst
37
+
38
+ candidates = [result_to_candidate(r) for r in valid_results]
39
+ pareto_candidates = compute_pareto_front(candidates, objectives)
40
+ sorted_pareto = sort_by_minimax_rank(pareto_candidates, objectives)
41
+ pareto_results = [candidate_to_result(c) for c in sorted_pareto]
42
+ stats["top_3_pareto"] = pareto_results[:3]
43
+ stats["pareto_front"] = pareto_results
44
+ stats["pareto_size"] = len(pareto_results)
45
+
46
+ return stats
47
+
48
+
49
+ def sample_failed_for_constraint(
50
+ latest_failed: Sequence[CandidateResult],
51
+ all_previous_failed: Sequence[CandidateResult],
52
+ max_examples: int,
53
+ rng: Optional[random.Random] = None,
54
+ ) -> List[CandidateResult]:
55
+ """Sample failures for constraint-instruction generation.
56
+
57
+ Always includes the latest failures; fills remaining slots with random
58
+ previous failures using the injected ``rng`` for reproducibility.
59
+ """
60
+ sampled = list(latest_failed)
61
+ if len(sampled) >= max_examples:
62
+ return sampled[:max_examples]
63
+
64
+ remaining = max_examples - len(sampled)
65
+ latest_ids = {id(r) for r in latest_failed}
66
+ previous = [r for r in all_previous_failed if id(r) not in latest_ids]
67
+ if previous and remaining > 0:
68
+ picker = rng or random
69
+ sampled.extend(picker.sample(previous, min(remaining, len(previous))))
70
+ return sampled
@@ -0,0 +1,100 @@
1
+ """What each guidance mechanism actually did, surfaced with the result.
2
+
3
+ The seam objects count their own behaviour (calls, rejections, authoring
4
+ attempts) and always have — but the counters lived as attributes on closures
5
+ the caller discarded, so no run could report what its guidance did. A number
6
+ that is counted but unreachable might as well not exist: the difference
7
+ between "guidance did not help" and "guidance never arrived" is exactly these
8
+ counters, and it must be readable off the ``SearchResult``.
9
+
10
+ The contract is deliberately small. Any seam object may carry:
11
+
12
+ ``telemetry`` an object whose ``as_dict()`` returns integer counters;
13
+ ``mechanism`` a short name for what the seam decides (``"chooser"``);
14
+ ``authored_by`` who authored the decisions — ``"llm"``, ``"rule"``, or
15
+ ``"none"``.
16
+
17
+ :func:`harvest_telemetry` gathers whatever is present and skips whatever is
18
+ not, so the unguided path reports an empty mechanism list rather than nothing
19
+ at all — a measured zero, distinguishable from "nobody looked".
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from dataclasses import dataclass
25
+ from typing import Any, Iterable, Mapping, Optional, Tuple
26
+
27
+ __all__ = ["MechanismTelemetry", "RunTelemetry", "harvest_telemetry"]
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class MechanismTelemetry:
32
+ """One mechanism's counters, labelled with who authored its decisions."""
33
+
34
+ mechanism: str
35
+ authored_by: str
36
+ counters: Mapping[str, int]
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class RunTelemetry:
41
+ """A run's mechanism counters plus its evaluation ledger.
42
+
43
+ ``real_evaluations`` counts evaluator invocations that were charged
44
+ against the budget. ``virtual_evaluations`` counts surrogate predictions —
45
+ free by construction, and reported separately precisely so the two can
46
+ never be conflated in a budget claim.
47
+
48
+ ``proxy_evaluations`` counts calls to a problem's CHEAPER evaluation
49
+ fidelity (``Problem.evaluate_proxy``). They are not free — they burn real
50
+ evaluator seconds — and they are not charged: the budget a claim is
51
+ denominated in counts full-fidelity evaluations. A campaign that spends
52
+ them must therefore report them, which is why they have a field of their
53
+ own here rather than a line in someone's log.
54
+ """
55
+
56
+ mechanisms: Tuple[MechanismTelemetry, ...] = ()
57
+ real_evaluations: int = 0
58
+ virtual_evaluations: int = 0
59
+ proxy_evaluations: int = 0
60
+
61
+
62
+ def harvest_telemetry(
63
+ sources: Iterable[Optional[Any]],
64
+ *,
65
+ real_evaluations: int = 0,
66
+ virtual_evaluations: int = 0,
67
+ proxy_evaluations: int = 0,
68
+ ) -> RunTelemetry:
69
+ """Collect telemetry from whichever *sources* carry it.
70
+
71
+ ``None`` entries and objects without a usable ``telemetry.as_dict()`` are
72
+ skipped silently: absence of counters is a legitimate state (the random
73
+ chooser, the statistical prior), not an error.
74
+ """
75
+
76
+ mechanisms = []
77
+ for source in sources:
78
+ if source is None:
79
+ continue
80
+ counter = getattr(source, "telemetry", None)
81
+ as_dict = getattr(counter, "as_dict", None)
82
+ if not callable(as_dict):
83
+ continue
84
+ counters = {str(k): int(v) for k, v in dict(as_dict()).items()}
85
+ mechanisms.append(
86
+ MechanismTelemetry(
87
+ mechanism=str(
88
+ getattr(source, "mechanism", None)
89
+ or getattr(source, "__name__", type(source).__name__)
90
+ ),
91
+ authored_by=str(getattr(source, "authored_by", "unrecorded")),
92
+ counters=counters,
93
+ )
94
+ )
95
+ return RunTelemetry(
96
+ mechanisms=tuple(mechanisms),
97
+ real_evaluations=int(real_evaluations),
98
+ virtual_evaluations=int(virtual_evaluations),
99
+ proxy_evaluations=int(proxy_evaluations),
100
+ )