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,704 @@
1
+ """What the run MEASURED, rendered so a model can reason about it.
2
+
3
+ Every authored mechanism in this package so far has been conditioned on
4
+ MEANING: the model reads a semantic card and a schema and writes an artifact
5
+ before a single evaluation exists. That is a static prior. It is worth a great
6
+ deal where a recallable domain prior is strong and it is worth exactly nothing
7
+ where one is not, and no amount of re-prompting changes which of those two a
8
+ venue is -- because the prompt never contains a measurement.
9
+
10
+ This module is the other half: it turns the run's own measured
11
+ ``(configuration -> objectives)`` trace into evidence a model can be asked to
12
+ reason over. It is deliberately a pure function of the trace:
13
+
14
+ - it never sees the problem, the evaluator, the cache or the budget, so
15
+ rendering evidence cannot spend one;
16
+ - what it emits is TEXT plus a digest of that text, so a run can record
17
+ exactly what the model was shown and no run can imply it reasoned over
18
+ measurements when it did not;
19
+ - it computes nothing a reader cannot recompute from the same rows.
20
+
21
+ Three things are rendered, in the order the evidence supports them:
22
+
23
+ ``front`` the non-dominated members measured so far -- the run's own
24
+ weight-free verdict on what is good, the same relation
25
+ selection already uses.
26
+ ``progress`` per objective, the best value in the first half of the trace
27
+ against the best in the second, so "what improved and what
28
+ did not" is a measurement and not an adjective.
29
+ ``effects`` per (locus, objective) rank correlation over the measured
30
+ rows: WHICH KNOB MOVES WHICH COST. This is the term a genetic
31
+ algorithm can only discover by stumbling and a reader of
32
+ twenty measured points should be able to name.
33
+
34
+ ``coverage`` rides along with ``effects``: which declared values a locus has
35
+ actually been measured at, so "this region is exhausted" is checkable.
36
+
37
+ :func:`render_elite_table` renders a fourth thing separately, because it is a
38
+ different KIND of statement and its consumers ask for it by name: value
39
+ occupancy among the non-dominated rows, against occupancy across the whole
40
+ trace. ``effects`` needs rows before it says anything true; occupancy is a
41
+ count over configurations and survives the tens of rows a mid-run reader
42
+ actually has. Its docstring carries the measurement that settled which of the
43
+ two to lead with.
44
+ """
45
+
46
+ from __future__ import annotations
47
+
48
+ import hashlib
49
+ import json
50
+ import math
51
+ from dataclasses import dataclass
52
+ from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple
53
+
54
+ from agent_evolve.core.problem import ObjectiveSpec
55
+ from agent_evolve.core.results import dominates
56
+ from agent_evolve.policies.genetic import Locus, loci_of, read_locus
57
+
58
+ __all__ = [
59
+ "MIN_EVIDENCE_ROWS",
60
+ "ELITE_ENRICHMENT_FLOOR",
61
+ "ELITE_MIN_COUNT",
62
+ "MeasuredRow",
63
+ "EvidenceView",
64
+ "front_of",
65
+ "spearman",
66
+ "locus_effects",
67
+ "render_measurement_evidence",
68
+ "render_elite_table",
69
+ "evidence_digest",
70
+ "WeightedProposal",
71
+ "PriorVerdict",
72
+ "parse_weighted_restriction",
73
+ "admit_weighted_restriction",
74
+ "apply_weighted_restriction",
75
+ "WEIGHTED_RESTRICTION_PROMPT",
76
+ ]
77
+
78
+ #: One measured candidate: its configuration, what the evaluator returned,
79
+ #: and whether it survived selection. Survival is the run's own weight-free
80
+ #: quality verdict and costs nothing extra to carry.
81
+ MeasuredRow = Tuple[Mapping[str, Any], Mapping[str, float], bool]
82
+
83
+ #: The fewest measured rows from which this module can render ONE determinable
84
+ #: statement about which knob moves which cost -- ``spearman`` is undefined
85
+ #: below three pairs, so evidence built from fewer rows carries a front and a
86
+ #: progress line and not a single ``effects`` entry, which is the term the
87
+ #: locus-importance channel exists to act on. It is therefore the honest floor
88
+ #: for "the evidence gate can be met", and it is exported so a consumer states
89
+ #: its threshold in terms of what the evidence can actually support rather
90
+ #: than picking a number.
91
+ MIN_EVIDENCE_ROWS = 3
92
+
93
+ #: The seam a CONTROL arm wraps. A view receives the rows this run measured
94
+ #: and returns the rows the model will be shown. The identity view is the
95
+ #: product; a view that returns another run's rows -- same count, same shape,
96
+ #: same cost, wrong run -- is the shuffled-evidence control that says whether
97
+ #: any measured gain comes from reasoning over THIS run's evidence or merely
98
+ #: from making another call. It lives here, as a declared parameter, because a
99
+ #: mechanism whose control cannot be built without editing the product is a
100
+ #: mechanism whose control will not be built.
101
+ EvidenceView = Callable[[Sequence[MeasuredRow]], Sequence[MeasuredRow]]
102
+
103
+
104
+ def front_of(
105
+ rows: Sequence[MeasuredRow],
106
+ specs: Sequence[ObjectiveSpec],
107
+ ) -> List[MeasuredRow]:
108
+ """The non-dominated subset of *rows*, in measurement order."""
109
+
110
+ kept: List[MeasuredRow] = []
111
+ for row in rows:
112
+ if any(dominates(dict(other[1]), dict(row[1]), specs)
113
+ for other in rows if other is not row):
114
+ continue
115
+ kept.append(row)
116
+ return kept
117
+
118
+
119
+ def _ranks(values: Sequence[float]) -> List[float]:
120
+ """Average ranks, so ties do not manufacture correlation."""
121
+
122
+ order = sorted(range(len(values)), key=lambda i: values[i])
123
+ out = [0.0] * len(values)
124
+ i = 0
125
+ while i < len(order):
126
+ j = i
127
+ while j + 1 < len(order) and values[order[j + 1]] == values[order[i]]:
128
+ j += 1
129
+ mean_rank = (i + j) / 2.0 + 1.0
130
+ for k in range(i, j + 1):
131
+ out[order[k]] = mean_rank
132
+ i = j + 1
133
+ return out
134
+
135
+
136
+ def spearman(xs: Sequence[float], ys: Sequence[float]) -> Optional[float]:
137
+ """Rank correlation, or ``None`` when it is not defined.
138
+
139
+ Undefined means undefined and is reported as such: fewer than three
140
+ pairs, or a constant column, cannot produce a correlation and must not
141
+ produce a zero that reads like "measured, no effect".
142
+ """
143
+
144
+ if len(xs) != len(ys) or len(xs) < 3:
145
+ return None
146
+ if len(set(xs)) < 2 or len(set(ys)) < 2:
147
+ return None
148
+ rx, ry = _ranks(list(xs)), _ranks(list(ys))
149
+ mx = sum(rx) / len(rx)
150
+ my = sum(ry) / len(ry)
151
+ num = sum((a - mx) * (b - my) for a, b in zip(rx, ry))
152
+ den = math.sqrt(sum((a - mx) ** 2 for a in rx)
153
+ * sum((b - my) ** 2 for b in ry))
154
+ if den <= 0.0:
155
+ return None
156
+ return max(-1.0, min(1.0, num / den))
157
+
158
+
159
+ @dataclass(frozen=True)
160
+ class LocusEffect:
161
+ """One locus, and what the trace says it does to each objective."""
162
+
163
+ locus: str
164
+ #: objective name -> rank correlation between the locus's position in its
165
+ #: DECLARED value order and that objective. ``None`` where undefined.
166
+ correlation: Mapping[str, Optional[float]]
167
+ #: declared value -> how many measured rows hold it.
168
+ coverage: Mapping[str, int]
169
+ unmeasured: Tuple[str, ...]
170
+
171
+ @property
172
+ def strength(self) -> float:
173
+ return max((abs(v) for v in self.correlation.values() if v is not None),
174
+ default=0.0)
175
+
176
+
177
+ def locus_effects(
178
+ rows: Sequence[MeasuredRow],
179
+ specs: Sequence[ObjectiveSpec],
180
+ domains: Mapping[str, Sequence[Any]],
181
+ ) -> List[LocusEffect]:
182
+ """Per-locus rank correlations and coverage, strongest effect first.
183
+
184
+ The predictor is a locus's POSITION IN ITS DECLARED VALUE ORDER, not its
185
+ value: the declared order is the only ordering this package is allowed to
186
+ assume about a categorical domain, it is what the schema published, and it
187
+ is the same order the sampler draws from. Where a domain is genuinely
188
+ unordered the correlation is meaningless and the model is told the
189
+ coverage instead -- which is why both travel together.
190
+ """
191
+
192
+ if not rows:
193
+ return []
194
+ template = dict(rows[0][0])
195
+ effects: List[LocusEffect] = []
196
+ for locus in loci_of(template):
197
+ key = str(locus)
198
+ domain = list(domains.get(key) or ())
199
+ if len(domain) < 2:
200
+ continue
201
+ index_of = {_token(v): i for i, v in enumerate(domain)}
202
+ positions: List[float] = []
203
+ keep: List[int] = []
204
+ counts: Dict[str, int] = {_token(v): 0 for v in domain}
205
+ for i, (config, _obj, _s) in enumerate(rows):
206
+ try:
207
+ token = _token(read_locus(config, locus))
208
+ except Exception:
209
+ continue
210
+ if token not in index_of:
211
+ continue
212
+ counts[token] += 1
213
+ positions.append(float(index_of[token]))
214
+ keep.append(i)
215
+ correlation: Dict[str, Optional[float]] = {}
216
+ for spec in specs:
217
+ column = [float(rows[i][1].get(spec.name, 0.0)) for i in keep]
218
+ correlation[spec.name] = spearman(positions, column)
219
+ effects.append(LocusEffect(
220
+ locus=key,
221
+ correlation=correlation,
222
+ coverage=dict(counts),
223
+ unmeasured=tuple(t for t, c in counts.items() if c == 0),
224
+ ))
225
+ effects.sort(key=lambda e: (-e.strength, e.locus))
226
+ return effects
227
+
228
+
229
+ def _token(value: Any) -> str:
230
+ return value if isinstance(value, str) else json.dumps(value, default=str)
231
+
232
+
233
+ def _fmt(value: float) -> str:
234
+ return f"{float(value):.6g}"
235
+
236
+
237
+ def render_measurement_evidence(
238
+ rows: Sequence[MeasuredRow],
239
+ specs: Sequence[ObjectiveSpec],
240
+ domains: Mapping[str, Sequence[Any]],
241
+ *,
242
+ front_shown: int = 8,
243
+ effects_shown: int = 8,
244
+ charged: Optional[int] = None,
245
+ ) -> str:
246
+ """The measured trace as prompt text. Pure, and a pure function of *rows*.
247
+
248
+ Nothing here is an opinion. Every line is a count, a measured value or a
249
+ rank correlation over the rows passed in, so the same rows always render
250
+ the same bytes and :func:`evidence_digest` of that text identifies the
251
+ evidence a call was conditioned on.
252
+ """
253
+
254
+ rows = list(rows)
255
+ if not rows:
256
+ return " no candidate has been measured yet."
257
+ lines: List[str] = [
258
+ f" measured so far: {len(rows)} configurations"
259
+ + (f" ({charged} charged evaluations)" if charged is not None else ""),
260
+ ]
261
+
262
+ front = front_of(rows, specs)
263
+ lines.append(f" the current non-dominated front ({len(front)} of "
264
+ f"{len(rows)} measured):")
265
+ for config, objectives, _s in front[:front_shown]:
266
+ rendered = ", ".join(f"{s.name}={_fmt(objectives.get(s.name, 0.0))}"
267
+ for s in specs)
268
+ lines.append(f" {json.dumps(config, sort_keys=True, default=str)}"
269
+ f" -> {rendered}")
270
+ if len(front) > front_shown:
271
+ lines.append(f" ... and {len(front) - front_shown} more front members")
272
+
273
+ lines.append(" progress -- best value per objective, first half of the "
274
+ "run vs second half:")
275
+ half = max(1, len(rows) // 2)
276
+ for spec in specs:
277
+ early = [float(o.get(spec.name, 0.0)) for _c, o, _s in rows[:half]]
278
+ late = [float(o.get(spec.name, 0.0)) for _c, o, _s in rows[half:]]
279
+ pick = min if spec.goal == "min" else max
280
+ best_early = pick(early) if early else float("nan")
281
+ best_late = pick(late) if late else best_early
282
+ moved = ("IMPROVED" if late and pick([best_early, best_late]) == best_late
283
+ and best_late != best_early else "did not improve")
284
+ lines.append(f" {spec.name} ({spec.goal}imise): {_fmt(best_early)} "
285
+ f"-> {_fmt(best_late)} [{moved}]")
286
+
287
+ effects = locus_effects(rows, specs, domains)
288
+ if effects:
289
+ lines.append(" which parameter moves which cost -- rank correlation "
290
+ "between the parameter's position in its declared value "
291
+ "list and the measured cost (+1 = later values cost more, "
292
+ "-1 = later values cost less, '-' = not determinable "
293
+ "from these rows):")
294
+ for effect in effects[:effects_shown]:
295
+ body = ", ".join(
296
+ f"{name}={'-' if value is None else f'{value:+.2f}'}"
297
+ for name, value in effect.correlation.items())
298
+ unmeasured = (f"; never measured at: "
299
+ f"{', '.join(effect.unmeasured)}"
300
+ if effect.unmeasured else "; every declared value "
301
+ "has been measured")
302
+ lines.append(f" {effect.locus}: {body}{unmeasured}")
303
+ weak = [e.locus for e in effects[effects_shown:] if e.strength < 0.2]
304
+ if weak:
305
+ lines.append(f" (no determinable effect on any cost: "
306
+ f"{', '.join(weak[:12])})")
307
+ return "\n".join(lines)
308
+
309
+
310
+ #: How far a value's share among the front must exceed its share of the whole
311
+ #: trace before this table calls it informative. At the tens of rows a mid-run
312
+ #: revision actually has, a smaller gap is one row changing its mind.
313
+ ELITE_ENRICHMENT_FLOOR = 0.05
314
+
315
+ #: How often a value must appear among the front to be worth printing whatever
316
+ #: its enrichment. Twice is the fewest that is not a single draw.
317
+ ELITE_MIN_COUNT = 2
318
+
319
+
320
+ def render_elite_table(
321
+ rows: Sequence[MeasuredRow],
322
+ specs: Sequence[ObjectiveSpec],
323
+ domains: Mapping[str, Sequence[Any]],
324
+ *,
325
+ max_elite: int = 12,
326
+ ) -> str:
327
+ """What the non-dominated configurations are MADE OF. Pure, like the rest.
328
+
329
+ ``effects`` above answers "which knob moves which cost" with a per-locus
330
+ rank correlation, and that answer has a measured floor under it: it needs
331
+ rows. A mid-run revision has 40-90 of them spread over a two-dozen-field
332
+ space, and at that scale the correlations are noise -- the W1 pilot's
333
+ revision channel re-tilted them four times a run and moved the final result
334
+ by less than the revision draw's own sampling variance (2026-08-19, five
335
+ taped analog pairs: 2/5 by sign under both bet variants, and the same arm
336
+ on the same tape moving 0.29 between two draws). Re-reading a noisy
337
+ statistic more often does not make it a signal.
338
+
339
+ What DID separate, sealed, was a graded prior whose author never computed a
340
+ correlation: the format that seat worked in was value OCCUPANCY -- which
341
+ declared values the configurations that are already good are built out of,
342
+ read against how often the same value shows up in the run at large. It is a
343
+ count over configurations rather than a statistic over ranks, it assumes no
344
+ ordering on a categorical domain, and one more row cannot swing it the way
345
+ one more row swings a Spearman. This function renders that format.
346
+
347
+ The elite are the goal-aware rank-0 rows -- :func:`front_of`, the same
348
+ domination relation selection itself uses -- truncated to *max_elite* in
349
+ measurement order when the front is larger, so a run whose front is half
350
+ its trace still gets a table a model can read.
351
+
352
+ Occupancy is per FIELD, not per locus: a sequence field's elements pool
353
+ into one entry, because the weight table a revision writes is keyed per
354
+ field and a table per position would be a different prior for every genome
355
+ length. Pooling makes the denominator the number of in-domain SLOTS the
356
+ field contributes rather than the number of rows, which for a scalar field
357
+ is exactly the number of rows and so reduces to "count among elite / elite
358
+ size".
359
+
360
+ Only values worth the line are printed: a value whose elite share beats its
361
+ overall share by more than :data:`ELITE_ENRICHMENT_FLOOR`, or that the
362
+ front holds at least :data:`ELITE_MIN_COUNT` times. A field with no such
363
+ value is omitted entirely and the closing line counts how many fields that
364
+ was, so an omission reads as "measured, said nothing" rather than as an
365
+ absence the reader has to notice.
366
+
367
+ NO objective value appears anywhere in this rendering. The front's scores
368
+ are the business of the section above; this one says only where the front
369
+ sits in the search space, so occupancy cannot be misread as a score.
370
+ """
371
+
372
+ rows = list(rows)
373
+ if not rows:
374
+ return " no candidate has been measured yet."
375
+
376
+ fields: List[str] = []
377
+ declared: Dict[str, List[str]] = {}
378
+ for locus in loci_of(dict(rows[0][0])):
379
+ if locus.field in declared:
380
+ continue
381
+ values = list(domains.get(str(locus)) or ())
382
+ if len(values) < 2:
383
+ continue
384
+ fields.append(locus.field)
385
+ declared[locus.field] = [_token(v) for v in values]
386
+
387
+ def _tally(subset: Sequence[MeasuredRow]
388
+ ) -> Tuple[Dict[str, Dict[str, int]], Dict[str, int]]:
389
+ counts = {name: {token: 0 for token in declared[name]}
390
+ for name in fields}
391
+ slots = {name: 0 for name in fields}
392
+ for config, _objectives, _survived in subset:
393
+ for locus in loci_of(dict(config)):
394
+ held = counts.get(locus.field)
395
+ if held is None:
396
+ continue
397
+ try:
398
+ token = _token(read_locus(config, locus))
399
+ except Exception:
400
+ continue
401
+ if token not in held:
402
+ continue
403
+ held[token] += 1
404
+ slots[locus.field] += 1
405
+ return counts, slots
406
+
407
+ front = front_of(rows, specs)
408
+ elite = front[:max_elite]
409
+ elite_counts, elite_slots = _tally(elite)
410
+ all_counts, all_slots = _tally(rows)
411
+
412
+ scope = (f"the {len(elite)} non-dominated configurations"
413
+ if len(front) <= len(elite) else
414
+ f"the first {len(elite)} of {len(front)} non-dominated "
415
+ f"configurations, in measurement order")
416
+ lines: List[str] = [
417
+ f" which declared values {scope} hold, as a share of that set, "
418
+ f"against the same value's share across all {len(rows)} measured "
419
+ f"configurations. These are counts of configurations, never scores:"
420
+ ]
421
+
422
+ silent = 0
423
+ for name in fields:
424
+ shown: List[str] = []
425
+ for token in declared[name]:
426
+ elite_count = elite_counts[name][token]
427
+ elite_total = elite_slots[name]
428
+ elite_share = elite_count / elite_total if elite_total else 0.0
429
+ overall_total = all_slots[name]
430
+ overall_share = (all_counts[name][token] / overall_total
431
+ if overall_total else 0.0)
432
+ if not (elite_share - overall_share > ELITE_ENRICHMENT_FLOOR
433
+ or elite_count >= ELITE_MIN_COUNT):
434
+ continue
435
+ shown.append(f"{token} {elite_count}/{elite_total}="
436
+ f"{elite_share:.2f} vs {overall_share:.2f} overall "
437
+ f"({elite_share - overall_share:+.2f})")
438
+ if not shown:
439
+ silent += 1
440
+ continue
441
+ lines.append(f" {name}: " + "; ".join(shown))
442
+ lines.append(f" ({silent} of {len(fields)} parameters showed no value "
443
+ f"the front concentrates on)")
444
+ return "\n".join(lines)
445
+
446
+
447
+ def evidence_digest(text: str) -> str:
448
+ """The identity of one evidence rendering. Recorded on every call."""
449
+
450
+ return hashlib.sha256(text.encode("utf-8")).hexdigest()
451
+
452
+
453
+
454
+ # ================================================ the locus-importance channel
455
+
456
+ WEIGHTED_RESTRICTION_PROMPT = """You are advising a black-box multi-objective \
457
+ optimizer about WHERE IN ITS SEARCH SPACE to concentrate the rest of its
458
+ evaluations.
459
+
460
+ OBJECTIVES (name and direction):
461
+ {goals}
462
+
463
+ SEARCH SPACE -- every parameter and the values it may take:
464
+ {domains}
465
+
466
+ WHAT THE OPTIMIZER HAS ACTUALLY MEASURED SO FAR:
467
+
468
+ {evidence}
469
+
470
+ Read the measurements, not the parameter names. Decide which parameters
471
+ actually move the measured costs, and for those parameters how much of the
472
+ remaining budget each value deserves.
473
+
474
+ Reply with ONLY a JSON object, no prose and no code fence, of this shape:
475
+
476
+ {{"weight": {{"<parameter>": {{"<value>": <positive number>, ...}}, ...}},
477
+ "because": {{"<parameter>": "<one short sentence citing the measurements>"}}}}
478
+
479
+ Rules, and the harness checks every one of them:
480
+ - Name ONLY parameters that appear in the search space above, and ONLY values
481
+ that parameter declares. Anything else and the whole reply is REFUSED.
482
+ - Weights BIAS sampling; they exclude nothing. A value you do not mention
483
+ keeps weight 1. Every declared value stays reachable no matter what you
484
+ reply, so you cannot lose the optimum -- but you can waste budget, so
485
+ weight only what the measurements justify.
486
+ - Weights must be positive numbers, and within one parameter the heaviest
487
+ value may outweigh the lightest by at most {max_ratio}x (unmentioned values
488
+ count as weight 1). More concentration than that and the whole reply is
489
+ REFUSED: concentration is the point; a de-facto exclusion is not.
490
+ - Leave "weight" empty if the measurements do not justify biasing anything.
491
+ An honest empty answer is accepted and costs nothing."""
492
+
493
+
494
+ @dataclass(frozen=True)
495
+ class WeightedProposal:
496
+ """The model's reply, typed and UNJUDGED: a weight table and its reasons.
497
+
498
+ This is the parse product, not the prior. The gate judges it, and only an
499
+ ADMITTED proposal becomes a
500
+ :class:`~agent_evolve.policies.weighted_prior.WeightedRestriction` -- the
501
+ package's one generic graded-prior form -- constructed over the FULL
502
+ declared domains with strictly positive weights, so it excludes nothing
503
+ by construction.
504
+ """
505
+
506
+ #: parameter -> {value token -> weight}, exactly as replied.
507
+ weight: Mapping[str, Mapping[str, float]] = ()
508
+ because: Mapping[str, str] = ()
509
+
510
+ def as_note(self) -> Dict[str, Any]:
511
+ return {"weight": {k: {t: float(w) for t, w in dict(v).items()}
512
+ for k, v in dict(self.weight).items()},
513
+ "because": dict(self.because)}
514
+
515
+
516
+ @dataclass(frozen=True)
517
+ class PriorVerdict:
518
+ """Admitted or refused, and WHY. The gate refuses; it never trusts."""
519
+
520
+ admitted: bool
521
+ reason: str = ""
522
+ proposal: Optional[WeightedProposal] = None
523
+ #: The admitted prior: the generic graded form, over the full declared
524
+ #: domains, every weight strictly positive. ``None`` unless admitted.
525
+ prior: Optional["WeightedRestriction"] = None
526
+ #: The largest max/min weight ratio any one parameter carries, implicit
527
+ #: weights included. ``1.0`` is "biases nothing".
528
+ concentration: float = 1.0
529
+
530
+ def as_note(self) -> Dict[str, Any]:
531
+ note: Dict[str, Any] = {"admitted": bool(self.admitted),
532
+ "reason": self.reason,
533
+ "concentration": round(self.concentration, 4)}
534
+ if self.proposal is not None:
535
+ note.update(self.proposal.as_note())
536
+ return note
537
+
538
+
539
+ def parse_weighted_restriction(text: str) -> Optional[WeightedProposal]:
540
+ """The model's reply as a typed proposal, or ``None`` if it is not one.
541
+
542
+ Whole-reply acceptance, exactly as the authored-artifact gate does it: a
543
+ reply that is not a JSON object of the declared shape -- weights as
544
+ numbers, per parameter, per value -- is not repaired into one, because a
545
+ repaired prior is a prior nobody authored.
546
+ """
547
+
548
+ if not isinstance(text, str):
549
+ return None
550
+ body = text.strip()
551
+ if body.startswith("```"):
552
+ body = body.split("```")[1] if body.count("```") >= 2 else body
553
+ if body.startswith("json"):
554
+ body = body[4:]
555
+ start, end = body.find("{"), body.rfind("}")
556
+ if start < 0 or end <= start:
557
+ return None
558
+ try:
559
+ parsed = json.loads(body[start:end + 1])
560
+ except Exception:
561
+ return None
562
+ if not isinstance(parsed, dict):
563
+ return None
564
+ weight = parsed.get("weight")
565
+ if weight is None:
566
+ weight = {}
567
+ if not isinstance(weight, dict):
568
+ return None
569
+ typed: Dict[str, Dict[str, float]] = {}
570
+ for name, values in weight.items():
571
+ if not isinstance(values, dict):
572
+ return None
573
+ entry: Dict[str, float] = {}
574
+ for token, w in values.items():
575
+ if isinstance(w, bool) or not isinstance(w, (int, float)):
576
+ return None
577
+ entry[str(token)] = float(w)
578
+ typed[str(name)] = entry
579
+ because = parsed.get("because") or {}
580
+ if not isinstance(because, dict):
581
+ because = {}
582
+ return WeightedProposal(
583
+ weight=typed, because={str(k): str(v) for k, v in because.items()})
584
+
585
+
586
+ def admit_weighted_restriction(
587
+ proposal: Optional[WeightedProposal],
588
+ *,
589
+ domains: Mapping[str, Sequence[Any]],
590
+ max_weight_ratio: float = 8.0,
591
+ ) -> PriorVerdict:
592
+ """Refuse, or admit with the concentration measured. Never repair.
593
+
594
+ Refusals, in the order a wrong prior does damage:
595
+
596
+ ``unparsed`` there is no typed proposal to judge.
597
+ ``empty`` it weights nothing (or weights everything equally), so
598
+ there is nothing to measure and nothing to admit -- an
599
+ honest no-op, recorded as one.
600
+ ``undeclared`` a parameter or a value the schema never declared. The
601
+ whole reply goes, not the offending entry: a proposal
602
+ that is wrong about the schema has not read the schema,
603
+ and dropping only the bad line would admit the rest on
604
+ the strength of an author that demonstrably guessed.
605
+ ``invalid_weight`` a weight that is not a positive finite number. Same
606
+ whole-reply rule.
607
+ ``over_concentrated`` some parameter's heaviest value outweighs its
608
+ lightest (implicit ``1.0`` included) by more than
609
+ ``max_weight_ratio``. The cap is what keeps a graded
610
+ bias from becoming a de-facto exclusion.
611
+
612
+ What is NOT here, deliberately: ``excludes_front``. The admitted prior is
613
+ a :class:`~agent_evolve.policies.weighted_prior.WeightedRestriction`
614
+ built over the FULL declared domain of every named parameter with
615
+ strictly positive weights, so its support IS the declared domain: it can
616
+ exclude nothing, the predicate the hard form had to gate on is satisfied
617
+ structurally, and the W1 vacuous-or-veto pathology (a large front leaves
618
+ nothing an admissible hard restriction may drop) has no lever to act on.
619
+ """
620
+
621
+ if proposal is None:
622
+ return PriorVerdict(False, "unparsed")
623
+ weight = {k: dict(v) for k, v in dict(proposal.weight).items() if v}
624
+ if not weight:
625
+ return PriorVerdict(False, "empty", proposal)
626
+
627
+ declared = {str(k): [_token(v) for v in (values or ())]
628
+ for k, values in dict(domains).items()}
629
+ concentration = 1.0
630
+ for name, values in weight.items():
631
+ if name not in declared or not declared[name]:
632
+ return PriorVerdict(
633
+ False, f"undeclared parameter {name!r}", proposal)
634
+ allowed = set(declared[name])
635
+ for token, w in values.items():
636
+ if token not in allowed:
637
+ return PriorVerdict(
638
+ False, f"undeclared value for {name!r}", proposal)
639
+ if not math.isfinite(w) or w <= 0.0:
640
+ return PriorVerdict(
641
+ False, f"invalid_weight for {name!r}", proposal)
642
+ # Implicit 1.0 for every declared value the reply does not name.
643
+ full = [values.get(token, 1.0) for token in allowed]
644
+ ratio = max(full) / min(full)
645
+ concentration = max(concentration, ratio)
646
+
647
+ if concentration <= 1.0:
648
+ return PriorVerdict(False, "empty", proposal, concentration=concentration)
649
+ if concentration > float(max_weight_ratio):
650
+ return PriorVerdict(
651
+ False, f"over_concentrated ({concentration:.3g}x > "
652
+ f"{float(max_weight_ratio):g}x)", proposal,
653
+ concentration=concentration)
654
+
655
+ from agent_evolve.policies.weighted_prior import WeightedRestriction
656
+
657
+ weighted: Dict[str, Tuple[Tuple[Any, ...], Tuple[float, ...]]] = {}
658
+ for name, values in weight.items():
659
+ declared_values = tuple(dict(domains)[name])
660
+ weighted[name] = (declared_values,
661
+ tuple(float(values.get(_token(v), 1.0))
662
+ for v in declared_values))
663
+ return PriorVerdict(True, "admitted", proposal,
664
+ prior=WeightedRestriction(weighted),
665
+ concentration=concentration)
666
+
667
+
668
+ def apply_weighted_restriction(
669
+ domains: Mapping[str, Sequence[Any]],
670
+ prior: "WeightedRestriction",
671
+ ) -> Dict[str, List[Any]]:
672
+ """*domains* with sampling mass biased by an ADMITTED *prior*.
673
+
674
+ The generic ``weights_for`` seam biases draws the HARNESS makes; an
675
+ authored sampler draws from whatever list it is handed, so for that path
676
+ the bias must live in the list itself. It is mechanical and
677
+ sampler-agnostic: each declared value appears a number of times
678
+ proportional to its weight (implicit ``1.0`` where unnamed), scaled so
679
+ the lightest value appears exactly once. A generator that draws uniformly
680
+ from the list it is given -- which is all the authored-sampler contract
681
+ promises -- therefore lands its mass where the weights say, and EVERY
682
+ declared value remains present, so nothing is excluded and validation
683
+ against the declared domains never sees an illegal value. The admission
684
+ cap on the weight ratio is also the cap on how long these lists can grow.
685
+ """
686
+
687
+ entries = dict(getattr(prior, "weighted", {}) or {})
688
+ out: Dict[str, List[Any]] = {}
689
+ for name, values in dict(domains).items():
690
+ entry = entries.get(name)
691
+ if not entry:
692
+ out[name] = list(values)
693
+ continue
694
+ table = {_token(v): float(w) for v, w in zip(*entry)}
695
+ per_value = [(v, table.get(_token(v), 1.0)) for v in values]
696
+ lightest = min((w for _v, w in per_value), default=1.0)
697
+ if lightest <= 0.0: # admission forbids this;
698
+ out[name] = list(values) # degrade, never divide by 0
699
+ continue
700
+ biased: List[Any] = []
701
+ for v, w in per_value:
702
+ biased.extend([v] * max(1, round(w / lightest)))
703
+ out[name] = biased
704
+ return out