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,2308 @@
1
+ """High-level agentic generation over the provider-neutral structured port.
2
+
3
+ This module owns only schema construction and translation. The injected
4
+ callable owns execution (and may itself be backed by a retrying queue), so this
5
+ adapter performs no provider setup, I/O, retrying, or exception translation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import copy
11
+ import hashlib
12
+ import json
13
+ from collections.abc import Awaitable, Callable
14
+ from dataclasses import dataclass
15
+ from typing import Annotated, Any, ClassVar, Generic, Literal, TypeVar, cast, get_args
16
+
17
+ from pydantic import (
18
+ BaseModel,
19
+ ConfigDict,
20
+ Field,
21
+ StringConstraints,
22
+ create_model,
23
+ field_validator,
24
+ model_validator,
25
+ )
26
+ from pydantic_core import PydanticCustomError
27
+
28
+ from agent_evolve.domain.llm_task_queue import ValidationIssueReasonCode
29
+ from agent_evolve.domain.patch import ArrayIndex, JsonPath, ObjectKey
30
+ from agent_evolve.domain.finite_variation import (
31
+ FiniteVariationContract,
32
+ validate_finite_variation_contract,
33
+ )
34
+ from agent_evolve.domain.typed_json import (
35
+ FrozenJsonValue,
36
+ freeze_json,
37
+ is_json_scalar,
38
+ thaw_json,
39
+ typed_json_equal,
40
+ )
41
+ from agent_evolve.policies.variation.typed_patch import (
42
+ replace_existing_path,
43
+ value_at_path,
44
+ )
45
+ from agent_evolve.ports.agentic_generator import (
46
+ AgenticCallTelemetry,
47
+ AtomicMutationDraft,
48
+ AtomicMutationOutputContract,
49
+ CANDIDATE_COMPONENT_PATH_CONTRACT,
50
+ CandidateDraft,
51
+ ConflictResolutionDraft,
52
+ ExactParentCrossoverDraft,
53
+ ExactParentCrossoverOutputContract,
54
+ FiniteVariationSelectionDraft,
55
+ InsightDraft,
56
+ MetricComparisonAnchor,
57
+ MetricComparisonAnchorKind,
58
+ MetricEffectDirection,
59
+ MetricEffectPrediction,
60
+ ReflectionConsumerScope,
61
+ ReflectionEvidenceCatalog,
62
+ ReflectionGenerationRequest,
63
+ ReflectionGenerationResult,
64
+ ReflectionInsightContract,
65
+ ReflectionInsightKind,
66
+ SourceAttribution,
67
+ TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT,
68
+ VariationGenerationRequest,
69
+ VariationGenerationResult,
70
+ validate_reflection_evidence_catalog_result,
71
+ )
72
+ from agent_evolve.ports.structured_generator import (
73
+ MAX_PROMPT_UTF8_BYTES,
74
+ StructuredGenerationRequest,
75
+ StructuredGenerationResponse,
76
+ StructuredPromptLineage,
77
+ identity_prompt_lineage,
78
+ )
79
+
80
+
81
+ CANDIDATE_PROPOSAL_TOOL_NAME = "return_candidate_proposal"
82
+ ATOMIC_MUTATION_TOOL_NAME = "return_atomic_mutation"
83
+ FINITE_VARIATION_SELECTION_TOOL_NAME = "select_finite_variation_option"
84
+ EXACT_PARENT_CROSSOVER_TOOL_NAME = "select_parent_crossover_loci"
85
+ REFLECTION_TOOL_NAME = "return_reflection_insights"
86
+
87
+ _CONFIGURATION_ROOT_DESCRIPTION = (
88
+ "The complete proposed candidate configuration. This field's value, rather "
89
+ "than the enclosing proposal object, is JSON-path root '$' for "
90
+ "intended_changes and source_attribution."
91
+ )
92
+ _INTENDED_CHANGES_DESCRIPTION = (
93
+ "Candidate-component JSON paths describing the intended edits. "
94
+ + CANDIDATE_COMPONENT_PATH_CONTRACT
95
+ )
96
+ _SOURCE_ATTRIBUTION_DESCRIPTION = (
97
+ "Source claims at candidate-component JSON paths. "
98
+ + CANDIDATE_COMPONENT_PATH_CONTRACT
99
+ )
100
+ _TWO_PARENT_INTENDED_CHANGES_DESCRIPTION = (
101
+ _INTENDED_CHANGES_DESCRIPTION + " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
102
+ )
103
+ _TWO_PARENT_SOURCE_ATTRIBUTION_DESCRIPTION = (
104
+ _SOURCE_ATTRIBUTION_DESCRIPTION + " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
105
+ )
106
+
107
+ # These are local admission limits, not experiment policy. The provider sees a
108
+ # deliberately compact schema, while Pydantic retains these constraints in its
109
+ # core schema and validates them before a queue-owned attempt can succeed.
110
+ MAX_RATIONALE_CHARS = 16_384
111
+ MAX_CHANGE_CHARS = 4_096
112
+ MAX_INTENDED_CHANGES = 256
113
+ MAX_PATH_CHARS = 4_096
114
+ MAX_SOURCE_ATTRIBUTIONS = 4_096
115
+ MAX_ID_CHARS = 512
116
+ MAX_CLAIMED_INSIGHT_IDS = 1_024
117
+ MAX_PRESERVATION_IDS = 4_096
118
+ MAX_RELATION_ID_CHARS = 512
119
+ MAX_RESOLUTION_EXPLANATION_CHARS = 8_192
120
+ MAX_CONFLICT_RESOLUTIONS = 4_096
121
+ MAX_REFLECTION_TEXT_CHARS = 16_384
122
+ MAX_AFFECTED_PATHS = 256
123
+ MAX_EVIDENCE_CONTRAST_IDS = 256
124
+
125
+ _JSON_PATH_PATTERN = r"^\$([.\[].*)?$"
126
+ REFLECTION_WIRE_CONTRACT_REVISION = (
127
+ "reflection_wire_jsonpath_contract_v3_provider_grammar"
128
+ )
129
+
130
+ # Kept below the limit declarations so the rendered note and provider schema
131
+ # derive from the same generic admission constants as local validation.
132
+ REFLECTION_OUTPUT_CONTRACT_NOTE = (
133
+ "REFLECTION OUTPUT CONTRACT\n"
134
+ f"Revision: {REFLECTION_WIRE_CONTRACT_REVISION}. "
135
+ f"For every insight, affected_paths must contain 1 to {MAX_AFFECTED_PATHS} "
136
+ "JSON-style paths. Every path must be rooted at '$' "
137
+ "(examples: '$', '$.field', or '$[0]')."
138
+ )
139
+ REFLECTION_OUTPUT_CONTRACT_NOTE_SHA256 = hashlib.sha256(
140
+ REFLECTION_OUTPUT_CONTRACT_NOTE.encode("utf-8", errors="strict")
141
+ ).hexdigest()
142
+ REFLECTION_PROMPT_RENDERER_ID = "agent_evolve.reflection_prompt"
143
+ REFLECTION_PROMPT_RENDERER_REVISION = REFLECTION_WIRE_CONTRACT_REVISION
144
+ REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256 = hashlib.sha256(
145
+ (
146
+ "agent-evolve:reflection-prompt-renderer:v1\x00"
147
+ "algorithm=rstrip_then_two_newlines_then_contract_note_else_identity_at_"
148
+ "utf8_cap\x00"
149
+ f"wire_contract_revision={REFLECTION_WIRE_CONTRACT_REVISION}\x00"
150
+ f"contract_note_sha256={REFLECTION_OUTPUT_CONTRACT_NOTE_SHA256}\x00"
151
+ f"max_prompt_utf8_bytes={MAX_PROMPT_UTF8_BYTES}"
152
+ ).encode("utf-8", errors="strict")
153
+ ).hexdigest()
154
+ REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION = (
155
+ "reflection_wire_jsonpath_contract_v4_evidence_catalog"
156
+ )
157
+ REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256 = hashlib.sha256(
158
+ (
159
+ "agent-evolve:reflection-evidence-catalog-prompt-renderer:v1\x00"
160
+ "algorithm=rstrip_then_contract_note_then_canonical_catalog_or_fail_at_"
161
+ "utf8_cap\x00"
162
+ f"upstream_definition_sha256={REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256}"
163
+ "\x00citation_field=evidence_citation_keys\x00"
164
+ "citation_key_grammar=eNNNN\x00"
165
+ f"wire_contract_revision={REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION}"
166
+ "\x00"
167
+ f"max_prompt_utf8_bytes={MAX_PROMPT_UTF8_BYTES}"
168
+ ).encode("utf-8", errors="strict")
169
+ ).hexdigest()
170
+ REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION = "reflection_wire_semantic_contract_v5"
171
+ REFLECTION_SEMANTIC_PROMPT_RENDERER_DEFINITION_SHA256 = hashlib.sha256(
172
+ (
173
+ "agent-evolve:reflection-semantic-prompt-renderer:v1\x00"
174
+ "algorithm=legacy_note_then_semantic_contract_then_optional_catalog_or_fail_"
175
+ "at_utf8_cap\x00"
176
+ f"legacy_definition_sha256={REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256}"
177
+ "\x00"
178
+ "catalog_definition_sha256="
179
+ f"{REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256}\x00"
180
+ f"wire_contract_revision={REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION}\x00"
181
+ f"max_prompt_utf8_bytes={MAX_PROMPT_UTF8_BYTES}"
182
+ ).encode("utf-8", errors="strict")
183
+ ).hexdigest()
184
+
185
+
186
+ def render_reflection_prompt(
187
+ prompt: str,
188
+ evidence_catalog: ReflectionEvidenceCatalog | None = None,
189
+ *,
190
+ insight_contract: ReflectionInsightContract | None = None,
191
+ ) -> str:
192
+ """Render the exact provider prompt without exceeding the shared byte cap.
193
+
194
+ The structured schema always carries the rooted-path constraints. The
195
+ redundant natural-language note is appended only when the resulting prompt
196
+ remains valid for :class:`StructuredGenerationRequest`; otherwise the
197
+ original, already-valid prompt is preserved byte for byte.
198
+ """
199
+
200
+ if type(prompt) is not str:
201
+ raise TypeError("prompt must be an exact string")
202
+ if not prompt.strip():
203
+ raise ValueError("prompt must be non-empty")
204
+ prompt_bytes = prompt.encode("utf-8", errors="strict")
205
+ if len(prompt_bytes) > MAX_PROMPT_UTF8_BYTES:
206
+ raise ValueError("prompt exceeds MAX_PROMPT_UTF8_BYTES")
207
+ semantic_note: str | None = None
208
+ if insight_contract is not None:
209
+ if type(insight_contract) is not ReflectionInsightContract:
210
+ raise TypeError(
211
+ "insight_contract must be an exact ReflectionInsightContract or None"
212
+ )
213
+ ReflectionInsightContract.__post_init__(insight_contract)
214
+ if insight_contract.is_semantic_v3:
215
+ semantic_projection = {
216
+ "allowed_comparison_anchor_kinds": [
217
+ value.value
218
+ for value in insight_contract.allowed_comparison_anchor_kinds
219
+ ],
220
+ "allowed_consumer_scopes": [
221
+ value.value for value in insight_contract.allowed_consumer_scopes
222
+ ],
223
+ "allowed_decision_paths": list(insight_contract.allowed_decision_paths),
224
+ "allowed_factor_capabilities": list(
225
+ insight_contract.allowed_factor_capabilities
226
+ ),
227
+ "allowed_insight_kinds": [
228
+ value.value for value in insight_contract.allowed_insight_kinds
229
+ ],
230
+ "allowed_source_role_ids": list(
231
+ insight_contract.allowed_source_role_ids
232
+ ),
233
+ "required_metric_ids": list(insight_contract.required_metric_ids),
234
+ }
235
+ semantic_json = json.dumps(
236
+ semantic_projection,
237
+ ensure_ascii=True,
238
+ allow_nan=False,
239
+ separators=(",", ":"),
240
+ sort_keys=True,
241
+ )
242
+ semantic_note = (
243
+ "REFLECTION SEMANTIC CONTRACT\n"
244
+ f"Revision: {REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION}. "
245
+ "affected_paths contains only candidate decision paths from the "
246
+ "allowlist; never put a metric ID or factor capability there. "
247
+ "Put metric IDs only in effect_predictions and factor capability "
248
+ "IDs only in factor_capabilities. Every metric prediction requires "
249
+ "an explicit comparison_anchor and an adjudicable direction; never "
250
+ "emit unknown. Emit only exact closed-vocabulary values from this "
251
+ "request-scoped contract.\n"
252
+ f"Contract: {semantic_json}"
253
+ )
254
+ notes = [REFLECTION_OUTPUT_CONTRACT_NOTE]
255
+ if semantic_note is not None:
256
+ notes.append(semantic_note)
257
+ if evidence_catalog is not None:
258
+ if type(evidence_catalog) is not ReflectionEvidenceCatalog:
259
+ raise TypeError(
260
+ "evidence_catalog must be an exact ReflectionEvidenceCatalog or None"
261
+ )
262
+ ReflectionEvidenceCatalog.__post_init__(evidence_catalog)
263
+ catalog_json = json.dumps(
264
+ evidence_catalog.to_record(),
265
+ ensure_ascii=True,
266
+ allow_nan=False,
267
+ separators=(",", ":"),
268
+ sort_keys=True,
269
+ )
270
+ catalog_note = (
271
+ "REFLECTION EVIDENCE CITATION CATALOG\n"
272
+ f"Revision: {REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION}. "
273
+ "For evidence_citation_keys, emit only exact citation_key values "
274
+ "from this request-scoped catalog. Never emit a contrast_id, prefix, "
275
+ "or invented key in that field. The adapter resolves each exact key "
276
+ "to its authenticated full contrast_id.\n"
277
+ f"Catalog: {catalog_json}"
278
+ )
279
+ notes.append(catalog_note)
280
+ catalog_rendered = f"{prompt.rstrip()}\n\n" + "\n\n".join(notes)
281
+ if (
282
+ len(catalog_rendered.encode("utf-8", errors="strict"))
283
+ > MAX_PROMPT_UTF8_BYTES
284
+ ):
285
+ raise ValueError(
286
+ "reflection evidence catalog cannot fit in the provider prompt"
287
+ )
288
+ return catalog_rendered
289
+ rendered = f"{prompt.rstrip()}\n\n" + "\n\n".join(notes)
290
+ if len(rendered.encode("utf-8", errors="strict")) <= MAX_PROMPT_UTF8_BYTES:
291
+ return rendered
292
+ if semantic_note is not None:
293
+ raise ValueError("reflection semantic contract cannot fit in provider prompt")
294
+ return prompt
295
+
296
+
297
+ _Rationale = Annotated[
298
+ str,
299
+ StringConstraints(
300
+ strict=True,
301
+ strip_whitespace=True,
302
+ min_length=1,
303
+ max_length=MAX_RATIONALE_CHARS,
304
+ ),
305
+ ]
306
+ _Change = Annotated[
307
+ str,
308
+ StringConstraints(
309
+ strict=True,
310
+ strip_whitespace=True,
311
+ min_length=1,
312
+ max_length=MAX_CHANGE_CHARS,
313
+ ),
314
+ ]
315
+ _Path = Annotated[
316
+ str,
317
+ StringConstraints(
318
+ strict=True,
319
+ strip_whitespace=True,
320
+ min_length=1,
321
+ max_length=MAX_PATH_CHARS,
322
+ pattern=_JSON_PATH_PATTERN,
323
+ ),
324
+ ]
325
+ _Identifier = Annotated[
326
+ str,
327
+ StringConstraints(
328
+ strict=True,
329
+ strip_whitespace=True,
330
+ min_length=1,
331
+ max_length=MAX_ID_CHARS,
332
+ ),
333
+ ]
334
+ _RelationIdentifier = Annotated[
335
+ str,
336
+ StringConstraints(
337
+ strict=True,
338
+ strip_whitespace=True,
339
+ min_length=1,
340
+ max_length=MAX_RELATION_ID_CHARS,
341
+ ),
342
+ ]
343
+ _ResolutionExplanation = Annotated[
344
+ str,
345
+ StringConstraints(
346
+ strict=True,
347
+ strip_whitespace=True,
348
+ min_length=1,
349
+ max_length=MAX_RESOLUTION_EXPLANATION_CHARS,
350
+ ),
351
+ ]
352
+ _ReflectionText = Annotated[
353
+ str,
354
+ StringConstraints(
355
+ strict=True,
356
+ strip_whitespace=True,
357
+ min_length=1,
358
+ max_length=MAX_REFLECTION_TEXT_CHARS,
359
+ ),
360
+ ]
361
+ _ContrastIdentifier = Annotated[
362
+ str,
363
+ StringConstraints(
364
+ strict=True,
365
+ pattern=r"^[0-9a-f]{64}$",
366
+ ),
367
+ ]
368
+ _EvidenceCitationKey = Annotated[
369
+ str,
370
+ StringConstraints(
371
+ strict=True,
372
+ pattern=r"^e[0-9]{4}$",
373
+ ),
374
+ ]
375
+
376
+ _STRICT_MODEL_CONFIG = ConfigDict(
377
+ extra="forbid",
378
+ strict=True,
379
+ frozen=True,
380
+ )
381
+
382
+
383
+ def _path_text(path: JsonPath) -> str:
384
+ parts = ["$"]
385
+ for segment in path.segments:
386
+ if type(segment) is ObjectKey:
387
+ parts.append(f".{segment.value}")
388
+ elif type(segment) is ArrayIndex:
389
+ parts.append(f"[{segment.value}]")
390
+ else: # pragma: no cover - JsonPath closes the segment union.
391
+ raise AssertionError("unsupported path segment")
392
+ return "".join(parts)
393
+
394
+
395
+ def _local_ref_value(root: dict[str, Any], reference: str) -> dict[str, Any]:
396
+ """Resolve one local JSON pointer without accepting external schemas."""
397
+
398
+ if type(reference) is not str or not reference.startswith("#/"):
399
+ raise ValueError("atomic leaf schema contains a non-local $ref")
400
+ current: object = root
401
+ for raw_token in reference[2:].split("/"):
402
+ token = raw_token.replace("~1", "/").replace("~0", "~")
403
+ if type(current) is not dict or token not in current:
404
+ raise ValueError("atomic leaf schema contains an unresolved $ref")
405
+ current = current[token]
406
+ if type(current) is not dict:
407
+ raise ValueError("atomic leaf $ref must resolve to an object schema")
408
+ return current
409
+
410
+
411
+ def _dereference_schema_top(
412
+ schema: dict[str, Any],
413
+ root: dict[str, Any],
414
+ *,
415
+ references: tuple[str, ...] = (),
416
+ ) -> dict[str, Any]:
417
+ """Expand a top-level local ref while detecting recursive definitions."""
418
+
419
+ if "$ref" not in schema:
420
+ return copy.deepcopy(schema)
421
+ reference = schema["$ref"]
422
+ if type(reference) is not str:
423
+ raise ValueError("atomic leaf schema $ref must be a string")
424
+ if reference in references:
425
+ raise ValueError("atomic leaf schema contains a recursive $ref")
426
+ resolved = _dereference_schema_top(
427
+ _local_ref_value(root, reference),
428
+ root,
429
+ references=references + (reference,),
430
+ )
431
+ siblings = {key: value for key, value in schema.items() if key != "$ref"}
432
+ # Pydantic normally emits a bare local ref. Siblings are legal JSON Schema,
433
+ # but conflicting keys cannot be combined faithfully and therefore fail
434
+ # closed instead of weakening either constraint.
435
+ for key, value in siblings.items():
436
+ if key in resolved and resolved[key] != value:
437
+ raise ValueError("atomic leaf schema has conflicting $ref siblings")
438
+ resolved[key] = copy.deepcopy(value)
439
+ return resolved
440
+
441
+
442
+ def _descend_schema(
443
+ schema: dict[str, Any],
444
+ segment: ObjectKey | ArrayIndex,
445
+ root: dict[str, Any],
446
+ ) -> dict[str, Any]:
447
+ current = _dereference_schema_top(schema, root)
448
+ if type(segment) is ObjectKey:
449
+ properties = current.get("properties")
450
+ if type(properties) is dict and segment.value in properties:
451
+ child = properties[segment.value]
452
+ if type(child) is not dict:
453
+ raise ValueError("atomic object property has no object schema")
454
+ return child
455
+ else:
456
+ items = current.get("items")
457
+ if type(items) is dict:
458
+ return items
459
+ prefix_items = current.get("prefixItems")
460
+ if type(prefix_items) is list and segment.value < len(prefix_items):
461
+ child = prefix_items[segment.value]
462
+ if type(child) is not dict:
463
+ raise ValueError("atomic tuple item has no object schema")
464
+ return child
465
+
466
+ for composition_key in ("anyOf", "oneOf", "allOf"):
467
+ variants = current.get(composition_key)
468
+ if type(variants) is not list:
469
+ continue
470
+ descended: list[dict[str, Any]] = []
471
+ for variant in variants:
472
+ if type(variant) is not dict:
473
+ raise ValueError("atomic schema composition is malformed")
474
+ try:
475
+ descended.append(_descend_schema(variant, segment, root))
476
+ except ValueError:
477
+ continue
478
+ if not descended:
479
+ continue
480
+ if len(descended) == 1:
481
+ return descended[0]
482
+ return {composition_key: descended}
483
+ raise ValueError("atomic editable path cannot be resolved in candidate schema")
484
+
485
+
486
+ def _expand_local_refs(
487
+ value: object,
488
+ root: dict[str, Any],
489
+ *,
490
+ references: tuple[str, ...] = (),
491
+ ) -> object:
492
+ if type(value) is list:
493
+ return [_expand_local_refs(item, root, references=references) for item in value]
494
+ if type(value) is not dict:
495
+ return copy.deepcopy(value)
496
+ current = value
497
+ if "$ref" in current:
498
+ reference = current["$ref"]
499
+ if type(reference) is not str:
500
+ raise ValueError("atomic leaf schema $ref must be a string")
501
+ if reference in references:
502
+ raise ValueError("atomic leaf schema contains a recursive $ref")
503
+ current = _dereference_schema_top(
504
+ current,
505
+ root,
506
+ references=references,
507
+ )
508
+ references = references + (reference,)
509
+ return {
510
+ key: _expand_local_refs(item, root, references=references)
511
+ for key, item in current.items()
512
+ if key != "$defs"
513
+ }
514
+
515
+
516
+ def _schema_is_scalar(schema: dict[str, Any]) -> bool:
517
+ scalar_types = {"null", "boolean", "integer", "number", "string"}
518
+ if "const" in schema and not is_json_scalar(schema["const"]):
519
+ return False
520
+ enum = schema.get("enum")
521
+ if type(enum) is list and (
522
+ not enum or not all(is_json_scalar(item) for item in enum)
523
+ ):
524
+ return False
525
+ declared_type = schema.get("type")
526
+ if type(declared_type) is str and declared_type in scalar_types:
527
+ return True
528
+ if (
529
+ type(declared_type) is list
530
+ and declared_type
531
+ and all(type(item) is str and item in scalar_types for item in declared_type)
532
+ ):
533
+ return True
534
+ if "const" in schema:
535
+ return True
536
+ if type(enum) is list:
537
+ return True
538
+ for key in ("anyOf", "oneOf", "allOf"):
539
+ variants = schema.get(key)
540
+ if (
541
+ type(variants) is list
542
+ and variants
543
+ and all(type(item) is dict and _schema_is_scalar(item) for item in variants)
544
+ ):
545
+ return True
546
+ return False
547
+
548
+
549
+ def _atomic_leaf_schema(
550
+ candidate_model: type[BaseModel],
551
+ path: JsonPath,
552
+ ) -> dict[str, Any]:
553
+ """Resolve one exact candidate-model leaf into a closed scalar schema."""
554
+
555
+ root = candidate_model.model_json_schema(by_alias=False)
556
+ if type(root) is not dict:
557
+ raise TypeError("candidate_model must return an object JSON schema")
558
+ current = root
559
+ for segment in path.segments:
560
+ current = _descend_schema(current, segment, root)
561
+ expanded = _expand_local_refs(current, root)
562
+ if type(expanded) is not dict or not _schema_is_scalar(expanded):
563
+ raise ValueError(
564
+ "atomic editable path must resolve to a closed scalar JSON schema"
565
+ )
566
+ return expanded
567
+
568
+
569
+ def _exclude_current_scalar(
570
+ schema: dict[str, Any],
571
+ current: object,
572
+ ) -> dict[str, Any]:
573
+ """Make a provider-visible scalar schema exclude the observed parent value."""
574
+
575
+ frozen_current = freeze_json(current)
576
+ if not is_json_scalar(frozen_current):
577
+ raise ValueError("atomic parent leaf must be a typed-JSON scalar")
578
+ result = copy.deepcopy(schema)
579
+ enum = result.get("enum")
580
+ if type(enum) is list:
581
+ remaining = [
582
+ item
583
+ for item in enum
584
+ if not typed_json_equal(freeze_json(item), frozen_current)
585
+ ]
586
+ if len(remaining) != len(enum):
587
+ if not remaining:
588
+ raise ValueError("atomic leaf schema permits only the current value")
589
+ result["enum"] = remaining
590
+ return result
591
+ if "const" in result and typed_json_equal(
592
+ freeze_json(result["const"]), frozen_current
593
+ ):
594
+ raise ValueError("atomic leaf schema permits only the current value")
595
+ exclusion = {"not": {"const": thaw_json(frozen_current)}}
596
+ if "not" not in result:
597
+ result.update(exclusion)
598
+ return result
599
+ return {"allOf": [result, exclusion]}
600
+
601
+
602
+ def _restrict_scalar_options(
603
+ schema: dict[str, Any],
604
+ options: tuple[FrozenJsonValue, ...],
605
+ ) -> dict[str, Any]:
606
+ """Intersect a leaf schema with one exact provider-visible option order."""
607
+
608
+ if not options:
609
+ return copy.deepcopy(schema)
610
+ result = copy.deepcopy(schema)
611
+ result["enum"] = [thaw_json(option) for option in options]
612
+ return result
613
+
614
+
615
+ def _compact_candidate_json_schema(model: type[BaseModel]) -> dict[str, Any]:
616
+ """Return the small provider contract while preserving the model's core schema."""
617
+
618
+ properties: dict[str, object] = {}
619
+ for name in model.model_fields:
620
+ if name == "configuration":
621
+ field_schema: dict[str, object] = {
622
+ "type": "object",
623
+ "additionalProperties": True,
624
+ }
625
+ elif name == "design_rationale":
626
+ field_schema = {"type": "string"}
627
+ elif name == "intended_changes":
628
+ evidence_path_contract = CANDIDATE_COMPONENT_PATH_CONTRACT
629
+ field_description = model.model_fields[name].description
630
+ if (
631
+ field_description is not None
632
+ and TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT in field_description
633
+ ):
634
+ evidence_path_contract += " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
635
+ field_schema = {
636
+ "type": "array",
637
+ "items": {
638
+ "type": "string",
639
+ "description": evidence_path_contract,
640
+ },
641
+ }
642
+ elif name in {
643
+ "claimed_insight_ids",
644
+ "claimed_preservation_obligation_ids",
645
+ }:
646
+ field_schema = {"type": "array", "items": {"type": "string"}}
647
+ elif name == "source_attribution":
648
+ evidence_path_contract = CANDIDATE_COMPONENT_PATH_CONTRACT
649
+ field_description = model.model_fields[name].description
650
+ if (
651
+ field_description is not None
652
+ and TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT in field_description
653
+ ):
654
+ evidence_path_contract += " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
655
+ attribution_annotation = model.model_fields[name].annotation
656
+ attribution_args = get_args(attribution_annotation)
657
+ if (
658
+ len(attribution_args) != 1
659
+ or not isinstance(attribution_args[0], type)
660
+ or not issubclass(attribution_args[0], BaseModel)
661
+ ):
662
+ raise RuntimeError("source attribution field lost its item model")
663
+ source_annotation = attribution_args[0].model_fields["source"].annotation
664
+ source_values = get_args(source_annotation)
665
+ if not source_values or any(
666
+ type(value) is not str for value in source_values
667
+ ):
668
+ raise RuntimeError(
669
+ "source attribution field lost its closed vocabulary"
670
+ )
671
+ field_schema = {
672
+ "type": "array",
673
+ "items": {
674
+ "type": "object",
675
+ "properties": {
676
+ "path": {
677
+ "type": "string",
678
+ "description": evidence_path_contract,
679
+ },
680
+ "source": {
681
+ "type": "string",
682
+ "enum": list(source_values),
683
+ },
684
+ },
685
+ "required": ["path", "source"],
686
+ "additionalProperties": False,
687
+ },
688
+ }
689
+ elif name == "conflict_resolutions":
690
+ field_schema = {
691
+ "type": "array",
692
+ "items": {
693
+ "type": "object",
694
+ "properties": {
695
+ "relation_id": {"type": "string"},
696
+ "choice": {
697
+ "type": "string",
698
+ "enum": [
699
+ "choose_left",
700
+ "choose_right",
701
+ "synthesize",
702
+ "drop_both",
703
+ ],
704
+ },
705
+ "explanation": {"type": "string"},
706
+ },
707
+ "required": ["relation_id", "choice", "explanation"],
708
+ "additionalProperties": False,
709
+ },
710
+ }
711
+ else: # pragma: no cover - guarded by the closed dynamic field builder.
712
+ raise RuntimeError(f"unsupported candidate wire field: {name}")
713
+ field_description = model.model_fields[name].description
714
+ if field_description is not None:
715
+ field_schema["description"] = field_description
716
+ properties[name] = field_schema
717
+ required = [
718
+ name for name, field in model.model_fields.items() if field.is_required()
719
+ ]
720
+ schema: dict[str, Any] = {
721
+ "type": "object",
722
+ "properties": properties,
723
+ "additionalProperties": False,
724
+ }
725
+ if required:
726
+ schema["required"] = required
727
+ return schema
728
+
729
+
730
+ class _CompactCandidateSchemaBase(BaseModel):
731
+ """Full local validation with a provider-compatible JSON-schema projection."""
732
+
733
+ model_config = _STRICT_MODEL_CONFIG
734
+
735
+ @classmethod
736
+ def __get_pydantic_json_schema__(
737
+ cls,
738
+ _core_schema: Any,
739
+ _handler: Any,
740
+ ) -> dict[str, Any]:
741
+ return _compact_candidate_json_schema(cls)
742
+
743
+
744
+ _AtomicScalar = bool | int | float | str | None
745
+
746
+
747
+ class _CompactAtomicMutationSchemaBase(BaseModel):
748
+ """One scalar wire edit plus whole-candidate semantic validation."""
749
+
750
+ model_config = _STRICT_MODEL_CONFIG
751
+ expected_path_text: ClassVar[str]
752
+ replacement_schema: ClassVar[dict[str, Any]]
753
+ candidate_model: ClassVar[type[BaseModel]]
754
+ output_contract: ClassVar[AtomicMutationOutputContract]
755
+
756
+ @classmethod
757
+ def __get_pydantic_json_schema__(
758
+ cls,
759
+ _core_schema: Any,
760
+ _handler: Any,
761
+ ) -> dict[str, Any]:
762
+ return {
763
+ "type": "object",
764
+ "properties": {
765
+ "path": {
766
+ "type": "string",
767
+ "const": cls.expected_path_text,
768
+ },
769
+ "replacement": copy.deepcopy(cls.replacement_schema),
770
+ "design_rationale": {"type": "string"},
771
+ "claimed_insight_ids": {
772
+ "type": "array",
773
+ "items": {"type": "string"},
774
+ },
775
+ },
776
+ "required": ["path", "replacement", "design_rationale"],
777
+ "additionalProperties": False,
778
+ }
779
+
780
+ @model_validator(mode="after")
781
+ def _validate_atomic_candidate(self) -> "_CompactAtomicMutationSchemaBase":
782
+ contract = type(self).output_contract
783
+ if self.path != type(self).expected_path_text:
784
+ raise ValueError("atomic mutation returned the wrong path")
785
+ replacement = freeze_json(self.replacement)
786
+ if not is_json_scalar(replacement): # pragma: no cover - closed field union.
787
+ raise ValueError("atomic replacement must be a typed-JSON scalar")
788
+ old_value = value_at_path(
789
+ contract.parent_configuration,
790
+ contract.editable_path,
791
+ )
792
+ if typed_json_equal(old_value, replacement):
793
+ raise ValueError("atomic replacement must differ from the parent value")
794
+ if contract.replacement_options and not any(
795
+ typed_json_equal(replacement, option)
796
+ for option in contract.replacement_options
797
+ ):
798
+ raise ValueError(
799
+ "atomic replacement is outside the contracted option catalog"
800
+ )
801
+ target = replace_existing_path(
802
+ contract.parent_configuration,
803
+ contract.editable_path,
804
+ replacement,
805
+ )
806
+ validated = type(self).candidate_model.model_validate(
807
+ thaw_json(target),
808
+ strict=True,
809
+ by_alias=False,
810
+ by_name=True,
811
+ )
812
+ validated_frozen = freeze_json(_configuration_dict(validated))
813
+ if not typed_json_equal(validated_frozen, target):
814
+ raise ValueError("candidate validation changed the typed atomic target")
815
+ return self
816
+
817
+
818
+ class _CompactExactParentCrossoverSchemaBase(BaseModel):
819
+ """A proper donor-locus subset behind a tiny provider-visible schema."""
820
+
821
+ model_config = _STRICT_MODEL_CONFIG
822
+ allowed_locus_ids: ClassVar[tuple[str, ...]] = ()
823
+ claimable_insight_ids: ClassVar[tuple[str, ...]] = ()
824
+ forbidden_import_locus_sets: ClassVar[tuple[tuple[str, ...], ...]] = ()
825
+
826
+ @classmethod
827
+ def __get_pydantic_json_schema__(
828
+ cls,
829
+ _core_schema: Any,
830
+ _handler: Any,
831
+ ) -> dict[str, Any]:
832
+ locus_count = len(cls.allowed_locus_ids)
833
+ if locus_count < 2: # pragma: no cover - construction validates first.
834
+ raise RuntimeError("exact crossover wire lost its finite loci")
835
+ claimable_insight_ids = cls.claimable_insight_ids
836
+ claimed_items: dict[str, object] = {
837
+ "type": "string",
838
+ "minLength": 1,
839
+ "maxLength": MAX_ID_CHARS,
840
+ }
841
+ if claimable_insight_ids:
842
+ claimed_items["enum"] = list(claimable_insight_ids)
843
+ import_schema: dict[str, object] = {
844
+ "type": "array",
845
+ "description": (
846
+ "Import these exact donor-parent loci into the sealed "
847
+ "base parent. Omitted loci remain from the base parent. "
848
+ "Do not select any explicitly forbidden exact set."
849
+ ),
850
+ "items": {
851
+ "type": "string",
852
+ "enum": list(cls.allowed_locus_ids),
853
+ },
854
+ "uniqueItems": True,
855
+ "minItems": 1,
856
+ "maxItems": locus_count - 1,
857
+ }
858
+ if cls.forbidden_import_locus_sets:
859
+ # Each clause excludes one set independent of array order. With
860
+ # uniqueItems, equal cardinality plus containment of every member
861
+ # is exact set equality; no enumeration of the remaining action
862
+ # space is needed.
863
+ import_schema["allOf"] = [
864
+ {
865
+ "not": {
866
+ "allOf": [
867
+ {"minItems": len(forbidden)},
868
+ {"maxItems": len(forbidden)},
869
+ *(
870
+ {"contains": {"const": locus_id}}
871
+ for locus_id in forbidden
872
+ ),
873
+ ]
874
+ }
875
+ }
876
+ for forbidden in cls.forbidden_import_locus_sets
877
+ ]
878
+ return {
879
+ "type": "object",
880
+ "properties": {
881
+ "import_locus_ids": import_schema,
882
+ "claimed_insight_ids": {
883
+ "type": "array",
884
+ "items": claimed_items,
885
+ "uniqueItems": True,
886
+ "maxItems": len(claimable_insight_ids),
887
+ },
888
+ },
889
+ "required": ["import_locus_ids"],
890
+ "additionalProperties": False,
891
+ }
892
+
893
+ @model_validator(mode="after")
894
+ def _validate_exact_parent_subset(
895
+ self,
896
+ ) -> "_CompactExactParentCrossoverSchemaBase":
897
+ allowed = type(self).allowed_locus_ids
898
+ imported = tuple(self.import_locus_ids)
899
+ if not imported or len(imported) >= len(allowed):
900
+ raise ValueError("import_locus_ids must be a proper nonempty donor subset")
901
+ if len(set(imported)) != len(imported):
902
+ raise ValueError("import_locus_ids cannot contain duplicates")
903
+ if not set(imported).issubset(allowed):
904
+ raise ValueError("import_locus_ids escaped the sealed locus catalog")
905
+ if tuple(sorted(imported)) in type(self).forbidden_import_locus_sets:
906
+ raise ValueError(
907
+ "import_locus_ids matches a forbidden known-child materialization"
908
+ )
909
+ if len(set(self.claimed_insight_ids)) != len(self.claimed_insight_ids):
910
+ raise ValueError("claimed_insight_ids cannot contain duplicates")
911
+ if not set(self.claimed_insight_ids).issubset(type(self).claimable_insight_ids):
912
+ raise ValueError("claimed_insight_ids escaped the assigned insight catalog")
913
+ return self
914
+
915
+
916
+ class _CompactReflectionSchemaBase(BaseModel):
917
+ """Bounded local reflection validation behind one generic wire envelope."""
918
+
919
+ model_config = _STRICT_MODEL_CONFIG
920
+ available_contrast_ids: ClassVar[tuple[str, ...]] = ()
921
+ available_evidence_citation_keys: ClassVar[tuple[str, ...]] = ()
922
+ evidence_catalog_identity_sha256: ClassVar[str | None] = None
923
+ insight_contract: ClassVar[ReflectionInsightContract | None] = None
924
+ min_insights: ClassVar[int] = 0
925
+ max_insights: ClassVar[int] = 4
926
+
927
+ @model_validator(mode="after")
928
+ def _validate_unique_normalized_claims(self) -> "_CompactReflectionSchemaBase":
929
+ insights = tuple(getattr(self, "insights", ()))
930
+ claims = tuple(
931
+ " ".join(value.claim.strip().casefold().split()) for value in insights
932
+ )
933
+ if len(set(claims)) != len(claims):
934
+ raise ValueError("reflection insights must have distinct normalized claims")
935
+ return self
936
+
937
+ @classmethod
938
+ def __get_pydantic_json_schema__(
939
+ cls,
940
+ _core_schema: Any,
941
+ _handler: Any,
942
+ ) -> dict[str, Any]:
943
+ reflection_text_schema: dict[str, Any] = {
944
+ "type": "string",
945
+ "minLength": 1,
946
+ "maxLength": MAX_REFLECTION_TEXT_CHARS,
947
+ }
948
+ catalog_mode = bool(cls.available_evidence_citation_keys)
949
+ citation_field_name = (
950
+ "evidence_citation_keys" if catalog_mode else "evidence_contrast_ids"
951
+ )
952
+ citation_values = (
953
+ cls.available_evidence_citation_keys
954
+ if catalog_mode
955
+ else cls.available_contrast_ids
956
+ )
957
+ contrast_item_schema: dict[str, Any] = {
958
+ "type": "string",
959
+ "pattern": "^e[0-9]{4}$" if catalog_mode else "^[0-9a-f]{64}$",
960
+ }
961
+ if citation_values:
962
+ contrast_item_schema["enum"] = list(citation_values)
963
+ insight_properties: dict[str, Any] = {
964
+ "claim": copy.deepcopy(reflection_text_schema),
965
+ "trigger": copy.deepcopy(reflection_text_schema),
966
+ "mechanism": copy.deepcopy(reflection_text_schema),
967
+ "affected_paths": {
968
+ "type": "array",
969
+ "minItems": 1,
970
+ "maxItems": MAX_AFFECTED_PATHS,
971
+ "items": {
972
+ "type": "string",
973
+ "minLength": 1,
974
+ "maxLength": MAX_PATH_CHARS,
975
+ "pattern": _JSON_PATH_PATTERN,
976
+ "description": (
977
+ "A JSON-style path rooted at '$', such as '$.field' or '$[0]'."
978
+ ),
979
+ },
980
+ },
981
+ "evidence_summary": copy.deepcopy(reflection_text_schema),
982
+ citation_field_name: {
983
+ "type": "array",
984
+ "items": contrast_item_schema,
985
+ "uniqueItems": True,
986
+ "minItems": (1 if citation_values else 0),
987
+ "maxItems": (
988
+ min(
989
+ MAX_EVIDENCE_CONTRAST_IDS,
990
+ len(citation_values),
991
+ )
992
+ if citation_values
993
+ else 0
994
+ ),
995
+ },
996
+ "confidence": {
997
+ "type": "number",
998
+ "minimum": 0.0,
999
+ "maximum": 1.0,
1000
+ },
1001
+ }
1002
+ required = [
1003
+ "claim",
1004
+ "trigger",
1005
+ "mechanism",
1006
+ "affected_paths",
1007
+ "evidence_summary",
1008
+ citation_field_name,
1009
+ "confidence",
1010
+ ]
1011
+ contract = cls.insight_contract
1012
+ if contract is not None:
1013
+ ReflectionInsightContract.__post_init__(contract)
1014
+ comparison_properties: dict[str, Any] = {}
1015
+ comparison_required: list[str] = []
1016
+ if contract.is_semantic_v3:
1017
+ role_schema: dict[str, Any] = {"type": "null"}
1018
+ if contract.allowed_source_role_ids:
1019
+ role_schema = {
1020
+ "anyOf": [
1021
+ {
1022
+ "type": "string",
1023
+ "enum": list(contract.allowed_source_role_ids),
1024
+ },
1025
+ {"type": "null"},
1026
+ ]
1027
+ }
1028
+ comparison_properties["comparison_anchor"] = {
1029
+ "type": "object",
1030
+ "properties": {
1031
+ "kind": {
1032
+ "type": "string",
1033
+ "enum": [
1034
+ value.value
1035
+ for value in (contract.allowed_comparison_anchor_kinds)
1036
+ ],
1037
+ },
1038
+ "source_role_id": role_schema,
1039
+ },
1040
+ "required": ["kind", "source_role_id"],
1041
+ "additionalProperties": False,
1042
+ }
1043
+ comparison_required.append("comparison_anchor")
1044
+ insight_properties.update(
1045
+ {
1046
+ "effect_predictions": {
1047
+ "type": "array",
1048
+ "items": {
1049
+ "type": "object",
1050
+ "properties": {
1051
+ "metric_id": {
1052
+ "type": "string",
1053
+ "enum": list(contract.required_metric_ids),
1054
+ },
1055
+ "direction": {
1056
+ "type": "string",
1057
+ "enum": [
1058
+ direction.value
1059
+ for direction in MetricEffectDirection
1060
+ ],
1061
+ },
1062
+ **comparison_properties,
1063
+ },
1064
+ "required": [
1065
+ "metric_id",
1066
+ "direction",
1067
+ *comparison_required,
1068
+ ],
1069
+ "additionalProperties": False,
1070
+ },
1071
+ "uniqueItems": True,
1072
+ "minItems": len(contract.required_metric_ids),
1073
+ "maxItems": len(contract.required_metric_ids),
1074
+ },
1075
+ "recommended_option_families": {
1076
+ "type": "array",
1077
+ "items": {
1078
+ "type": "string",
1079
+ "enum": list(contract.allowed_option_families),
1080
+ },
1081
+ "uniqueItems": True,
1082
+ "minItems": 1,
1083
+ "maxItems": len(contract.allowed_option_families),
1084
+ },
1085
+ "action_template": copy.deepcopy(reflection_text_schema),
1086
+ "falsification_condition": copy.deepcopy(reflection_text_schema),
1087
+ }
1088
+ )
1089
+ if contract.is_semantic_v3:
1090
+ insight_properties["affected_paths"].update(
1091
+ {
1092
+ "uniqueItems": True,
1093
+ "maxItems": min(
1094
+ MAX_AFFECTED_PATHS,
1095
+ len(contract.allowed_decision_paths),
1096
+ ),
1097
+ }
1098
+ )
1099
+ insight_properties["affected_paths"]["items"]["enum"] = list(
1100
+ contract.allowed_decision_paths
1101
+ )
1102
+ insight_properties.update(
1103
+ {
1104
+ "insight_kind": {
1105
+ "type": "string",
1106
+ "enum": [
1107
+ value.value for value in contract.allowed_insight_kinds
1108
+ ],
1109
+ },
1110
+ "consumer_scopes": {
1111
+ "type": "array",
1112
+ "items": {
1113
+ "type": "string",
1114
+ "enum": [
1115
+ value.value
1116
+ for value in contract.allowed_consumer_scopes
1117
+ ],
1118
+ },
1119
+ "uniqueItems": True,
1120
+ "minItems": 1,
1121
+ "maxItems": len(contract.allowed_consumer_scopes),
1122
+ },
1123
+ "factor_capabilities": {
1124
+ "type": "array",
1125
+ "items": (
1126
+ {
1127
+ "type": "string",
1128
+ "enum": list(contract.allowed_factor_capabilities),
1129
+ }
1130
+ if contract.allowed_factor_capabilities
1131
+ else {"type": "string"}
1132
+ ),
1133
+ "uniqueItems": True,
1134
+ "minItems": 0,
1135
+ "maxItems": len(contract.allowed_factor_capabilities),
1136
+ },
1137
+ }
1138
+ )
1139
+ if contract.allowed_option_ids:
1140
+ insight_properties["recommended_option_ids"] = {
1141
+ "type": "array",
1142
+ "items": {
1143
+ "type": "string",
1144
+ "enum": list(contract.allowed_option_ids),
1145
+ },
1146
+ "uniqueItems": True,
1147
+ "minItems": 1,
1148
+ "maxItems": len(contract.allowed_option_ids),
1149
+ }
1150
+ required.extend(
1151
+ [
1152
+ "effect_predictions",
1153
+ "recommended_option_families",
1154
+ "action_template",
1155
+ "falsification_condition",
1156
+ ]
1157
+ )
1158
+ if contract.allowed_option_ids:
1159
+ required.append("recommended_option_ids")
1160
+ if contract.is_semantic_v3:
1161
+ required.extend(
1162
+ ["insight_kind", "consumer_scopes", "factor_capabilities"]
1163
+ )
1164
+ return {
1165
+ "type": "object",
1166
+ "properties": {
1167
+ "insights": {
1168
+ "type": "array",
1169
+ "minItems": cls.min_insights,
1170
+ "maxItems": cls.max_insights,
1171
+ "items": {
1172
+ "type": "object",
1173
+ "properties": insight_properties,
1174
+ "required": required,
1175
+ "additionalProperties": False,
1176
+ },
1177
+ }
1178
+ },
1179
+ "required": ["insights"],
1180
+ "additionalProperties": False,
1181
+ }
1182
+
1183
+
1184
+ class _SourceAttributionOutput(BaseModel):
1185
+ model_config = _STRICT_MODEL_CONFIG
1186
+
1187
+ path: _Path = Field(description=CANDIDATE_COMPONENT_PATH_CONTRACT)
1188
+ source: Literal["ancestor", "left", "right", "synthesized", "mutation"]
1189
+
1190
+
1191
+ class _TwoParentSourceAttributionOutput(BaseModel):
1192
+ """Operation-specific wire vocabulary accepted by executable crossover."""
1193
+
1194
+ model_config = _STRICT_MODEL_CONFIG
1195
+
1196
+ path: _Path = Field(description=CANDIDATE_COMPONENT_PATH_CONTRACT)
1197
+ source: Literal["left", "right", "synthesized"]
1198
+
1199
+
1200
+ class _ConflictResolutionOutput(BaseModel):
1201
+ model_config = _STRICT_MODEL_CONFIG
1202
+
1203
+ relation_id: _RelationIdentifier
1204
+ choice: Literal["choose_left", "choose_right", "synthesize", "drop_both"]
1205
+ explanation: _ResolutionExplanation
1206
+
1207
+
1208
+ class _ReflectionInsightOutput(BaseModel):
1209
+ model_config = _STRICT_MODEL_CONFIG
1210
+
1211
+ claim: _ReflectionText
1212
+ trigger: _ReflectionText
1213
+ mechanism: _ReflectionText
1214
+ affected_paths: list[_Path] = Field(
1215
+ min_length=1,
1216
+ max_length=MAX_AFFECTED_PATHS,
1217
+ )
1218
+ evidence_summary: _ReflectionText
1219
+ evidence_contrast_ids: list[_ContrastIdentifier] = Field(
1220
+ max_length=MAX_EVIDENCE_CONTRAST_IDS,
1221
+ )
1222
+ confidence: float = Field(strict=True, ge=0.0, le=1.0, allow_inf_nan=False)
1223
+
1224
+ @field_validator("evidence_contrast_ids")
1225
+ @classmethod
1226
+ def _unique_evidence_contrast_ids(cls, values: list[str]) -> list[str]:
1227
+ del cls
1228
+ if len(set(values)) != len(values):
1229
+ raise ValueError("evidence_contrast_ids cannot contain duplicates")
1230
+ return values
1231
+
1232
+
1233
+ class _CatalogReflectionInsightOutput(BaseModel):
1234
+ """Runtime shape for request-local citations before authenticated resolution."""
1235
+
1236
+ model_config = _STRICT_MODEL_CONFIG
1237
+
1238
+ claim: _ReflectionText
1239
+ trigger: _ReflectionText
1240
+ mechanism: _ReflectionText
1241
+ affected_paths: list[_Path] = Field(
1242
+ min_length=1,
1243
+ max_length=MAX_AFFECTED_PATHS,
1244
+ )
1245
+ evidence_summary: _ReflectionText
1246
+ evidence_citation_keys: list[_EvidenceCitationKey] = Field(
1247
+ max_length=MAX_EVIDENCE_CONTRAST_IDS,
1248
+ )
1249
+ confidence: float = Field(strict=True, ge=0.0, le=1.0, allow_inf_nan=False)
1250
+
1251
+ @field_validator("evidence_citation_keys")
1252
+ @classmethod
1253
+ def _unique_evidence_citation_keys(cls, values: list[str]) -> list[str]:
1254
+ del cls
1255
+ if len(set(values)) != len(values):
1256
+ raise ValueError("evidence_citation_keys cannot contain duplicates")
1257
+ return values
1258
+
1259
+
1260
+ class _MetricComparisonAnchorOutput(BaseModel):
1261
+ model_config = _STRICT_MODEL_CONFIG
1262
+
1263
+ kind: Literal[
1264
+ "current_parent",
1265
+ "named_source_role",
1266
+ "common_ancestor",
1267
+ "frozen_archive_incumbent",
1268
+ ]
1269
+ source_role_id: _Identifier | None = None
1270
+
1271
+ @model_validator(mode="after")
1272
+ def _role_matches_anchor_kind(self) -> "_MetricComparisonAnchorOutput":
1273
+ if self.kind == "named_source_role":
1274
+ if self.source_role_id is None:
1275
+ raise PydanticCustomError(
1276
+ ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
1277
+ "named_source_role anchors require a source_role_id",
1278
+ )
1279
+ elif self.source_role_id is not None:
1280
+ raise PydanticCustomError(
1281
+ ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
1282
+ "source_role_id is valid only for named_source_role anchors",
1283
+ )
1284
+ return self
1285
+
1286
+
1287
+ class _MetricEffectPredictionOutput(BaseModel):
1288
+ model_config = _STRICT_MODEL_CONFIG
1289
+
1290
+ metric_id: _Identifier
1291
+ direction: Literal["decrease", "increase", "unchanged", "unknown"]
1292
+ comparison_anchor: _MetricComparisonAnchorOutput | None = None
1293
+
1294
+
1295
+ def _validate_semantic_reflection_output(value: Any, output_type: type[Any]) -> None:
1296
+ """Apply request-scoped semantic vocabularies without benchmark knowledge."""
1297
+
1298
+ semantic_fields_present = (
1299
+ value.insight_kind is not None
1300
+ or bool(value.consumer_scopes)
1301
+ or bool(value.factor_capabilities)
1302
+ or any(
1303
+ prediction.comparison_anchor is not None
1304
+ for prediction in value.effect_predictions
1305
+ )
1306
+ )
1307
+ if not output_type.semantic_v3:
1308
+ if semantic_fields_present:
1309
+ raise PydanticCustomError(
1310
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1311
+ "semantic fields require a v3 reflection insight contract",
1312
+ )
1313
+ return
1314
+ if value.insight_kind not in output_type.allowed_insight_kinds:
1315
+ raise PydanticCustomError(
1316
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1317
+ "insight_kind escaped the request vocabulary",
1318
+ )
1319
+ scopes = tuple(value.consumer_scopes)
1320
+ if not scopes or len(set(scopes)) != len(scopes):
1321
+ raise PydanticCustomError(
1322
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1323
+ "consumer_scopes must be nonempty and unique",
1324
+ )
1325
+ if not set(scopes).issubset(output_type.allowed_consumer_scopes):
1326
+ raise PydanticCustomError(
1327
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1328
+ "consumer_scopes escaped the request vocabulary",
1329
+ )
1330
+ paths = tuple(value.affected_paths)
1331
+ if len(set(paths)) != len(paths):
1332
+ raise PydanticCustomError(
1333
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1334
+ "affected_paths cannot contain duplicates",
1335
+ )
1336
+ if not set(paths).issubset(output_type.allowed_decision_paths):
1337
+ raise PydanticCustomError(
1338
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1339
+ "affected_paths escaped the decision-path vocabulary",
1340
+ )
1341
+ capabilities = tuple(value.factor_capabilities)
1342
+ if len(set(capabilities)) != len(capabilities):
1343
+ raise PydanticCustomError(
1344
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1345
+ "factor_capabilities cannot contain duplicates",
1346
+ )
1347
+ if not set(capabilities).issubset(output_type.allowed_factor_capabilities):
1348
+ raise PydanticCustomError(
1349
+ ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
1350
+ "factor_capabilities escaped the request vocabulary",
1351
+ )
1352
+ for prediction in value.effect_predictions:
1353
+ if prediction.direction == "unknown":
1354
+ raise PydanticCustomError(
1355
+ ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
1356
+ "v3 lifecycle hypotheses require an adjudicable metric direction",
1357
+ )
1358
+ anchor = prediction.comparison_anchor
1359
+ if anchor is None:
1360
+ raise PydanticCustomError(
1361
+ ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
1362
+ "v3 effect predictions require an explicit comparison_anchor",
1363
+ )
1364
+ if anchor.kind not in output_type.allowed_comparison_anchor_kinds:
1365
+ raise PydanticCustomError(
1366
+ ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
1367
+ "comparison anchor escaped the request vocabulary",
1368
+ )
1369
+ if (
1370
+ anchor.source_role_id is not None
1371
+ and anchor.source_role_id not in output_type.allowed_source_role_ids
1372
+ ):
1373
+ raise PydanticCustomError(
1374
+ ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
1375
+ "comparison source role escaped the request vocabulary",
1376
+ )
1377
+
1378
+
1379
+ def _validate_advanced_reflection_contract(
1380
+ value: Any,
1381
+ output_type: type[Any],
1382
+ ) -> None:
1383
+ """Validate the generic closed metric/action vocabulary with typed errors."""
1384
+
1385
+ metric_ids = tuple(item.metric_id for item in value.effect_predictions)
1386
+ if len(set(metric_ids)) != len(metric_ids) or set(metric_ids) != set(
1387
+ output_type.required_metric_ids
1388
+ ):
1389
+ raise PydanticCustomError(
1390
+ ValidationIssueReasonCode.REFLECTION_METRIC_CONTRACT_VIOLATION.value,
1391
+ "effect_predictions must contain each required metric exactly once",
1392
+ )
1393
+ families = tuple(value.recommended_option_families)
1394
+ option_ids = tuple(value.recommended_option_ids)
1395
+ allowed_option_ids = output_type.allowed_option_ids
1396
+ action_invalid = (
1397
+ len(set(families)) != len(families)
1398
+ or not set(families).issubset(output_type.allowed_option_families)
1399
+ or len(set(option_ids)) != len(option_ids)
1400
+ or bool(allowed_option_ids) != bool(option_ids)
1401
+ or bool(option_ids) and not set(option_ids).issubset(allowed_option_ids)
1402
+ )
1403
+ if action_invalid:
1404
+ raise PydanticCustomError(
1405
+ ValidationIssueReasonCode.REFLECTION_ACTION_CONTRACT_VIOLATION.value,
1406
+ "recommended actions must be nonempty, unique, and request-scoped",
1407
+ )
1408
+ if all(item.direction == "unknown" for item in value.effect_predictions):
1409
+ raise PydanticCustomError(
1410
+ ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
1411
+ "an outcome-grounded insight must predict an adjudicable direction",
1412
+ )
1413
+ _validate_semantic_reflection_output(value, output_type)
1414
+
1415
+
1416
+ class _AdvancedReflectionInsightOutput(_ReflectionInsightOutput):
1417
+ """Strict runtime validator; the request supplies its closed vocabularies."""
1418
+
1419
+ required_metric_ids: ClassVar[tuple[str, ...]] = ()
1420
+ allowed_option_families: ClassVar[tuple[str, ...]] = ()
1421
+ allowed_option_ids: ClassVar[tuple[str, ...]] = ()
1422
+ allowed_decision_paths: ClassVar[tuple[str, ...]] = ()
1423
+ allowed_insight_kinds: ClassVar[tuple[str, ...]] = ()
1424
+ allowed_consumer_scopes: ClassVar[tuple[str, ...]] = ()
1425
+ allowed_comparison_anchor_kinds: ClassVar[tuple[str, ...]] = ()
1426
+ allowed_factor_capabilities: ClassVar[tuple[str, ...]] = ()
1427
+ allowed_source_role_ids: ClassVar[tuple[str, ...]] = ()
1428
+ semantic_v3: ClassVar[bool] = False
1429
+
1430
+ effect_predictions: list[_MetricEffectPredictionOutput]
1431
+ recommended_option_families: list[_Identifier] = Field(min_length=1)
1432
+ recommended_option_ids: list[_Identifier] = Field(default_factory=list)
1433
+ action_template: _ReflectionText
1434
+ falsification_condition: _ReflectionText
1435
+ insight_kind: _Identifier | None = None
1436
+ consumer_scopes: list[_Identifier] = Field(default_factory=list)
1437
+ factor_capabilities: list[_Identifier] = Field(default_factory=list)
1438
+
1439
+ @model_validator(mode="after")
1440
+ def _exact_advanced_contract(self) -> "_AdvancedReflectionInsightOutput":
1441
+ _validate_advanced_reflection_contract(self, type(self))
1442
+ return self
1443
+
1444
+
1445
+ class _CatalogAdvancedReflectionInsightOutput(_CatalogReflectionInsightOutput):
1446
+ """Advanced reflection contract over request-local evidence keys."""
1447
+
1448
+ required_metric_ids: ClassVar[tuple[str, ...]] = ()
1449
+ allowed_option_families: ClassVar[tuple[str, ...]] = ()
1450
+ allowed_option_ids: ClassVar[tuple[str, ...]] = ()
1451
+ allowed_decision_paths: ClassVar[tuple[str, ...]] = ()
1452
+ allowed_insight_kinds: ClassVar[tuple[str, ...]] = ()
1453
+ allowed_consumer_scopes: ClassVar[tuple[str, ...]] = ()
1454
+ allowed_comparison_anchor_kinds: ClassVar[tuple[str, ...]] = ()
1455
+ allowed_factor_capabilities: ClassVar[tuple[str, ...]] = ()
1456
+ allowed_source_role_ids: ClassVar[tuple[str, ...]] = ()
1457
+ semantic_v3: ClassVar[bool] = False
1458
+
1459
+ effect_predictions: list[_MetricEffectPredictionOutput]
1460
+ recommended_option_families: list[_Identifier] = Field(min_length=1)
1461
+ recommended_option_ids: list[_Identifier] = Field(default_factory=list)
1462
+ action_template: _ReflectionText
1463
+ falsification_condition: _ReflectionText
1464
+ insight_kind: _Identifier | None = None
1465
+ consumer_scopes: list[_Identifier] = Field(default_factory=list)
1466
+ factor_capabilities: list[_Identifier] = Field(default_factory=list)
1467
+
1468
+ @model_validator(mode="after")
1469
+ def _exact_advanced_contract(
1470
+ self,
1471
+ ) -> "_CatalogAdvancedReflectionInsightOutput":
1472
+ _validate_advanced_reflection_contract(self, type(self))
1473
+ return self
1474
+
1475
+
1476
+ ResponseT = TypeVar("ResponseT")
1477
+
1478
+
1479
+ @dataclass(frozen=True, slots=True)
1480
+ class AttemptedStructuredGenerationResponse(Generic[ResponseT]):
1481
+ """A successful structured response plus its outer queue attempt count.
1482
+
1483
+ A direct one-attempt runner can return ``StructuredGenerationResponse``
1484
+ unchanged. A queue-composition function can return this envelope so retry
1485
+ evidence is not lost while the shared one-attempt port remains truthful.
1486
+ """
1487
+
1488
+ response: StructuredGenerationResponse[ResponseT]
1489
+ attempt_count: int
1490
+
1491
+ def __post_init__(self) -> None:
1492
+ if type(self.response) is not StructuredGenerationResponse:
1493
+ raise TypeError("response must be an exact StructuredGenerationResponse")
1494
+ StructuredGenerationResponse.__post_init__(self.response)
1495
+ if type(self.attempt_count) is not int or self.attempt_count <= 0:
1496
+ raise ValueError("attempt_count must be a positive exact integer")
1497
+
1498
+
1499
+ LowLevelResult = (
1500
+ StructuredGenerationResponse[Any] | AttemptedStructuredGenerationResponse[Any]
1501
+ )
1502
+ LowLevelRunner = Callable[
1503
+ [StructuredGenerationRequest[Any]],
1504
+ Awaitable[LowLevelResult],
1505
+ ]
1506
+
1507
+
1508
+ def _candidate_proposal_type(
1509
+ candidate_model: type[BaseModel],
1510
+ operation: str,
1511
+ ) -> type[BaseModel]:
1512
+ if not isinstance(candidate_model, type) or not issubclass(
1513
+ candidate_model, BaseModel
1514
+ ):
1515
+ raise TypeError("candidate_model must be a Pydantic BaseModel subclass")
1516
+ if candidate_model is BaseModel or getattr(
1517
+ candidate_model, "__pydantic_root_model__", False
1518
+ ):
1519
+ raise TypeError("candidate_model must describe an object configuration")
1520
+ if getattr(candidate_model, "__parameters__", ()):
1521
+ raise TypeError("candidate_model must be a concrete Pydantic model")
1522
+ if type(operation) is not str or not operation.strip():
1523
+ raise ValueError("operation must be non-empty")
1524
+
1525
+ is_two_parent_crossover = operation == "two_parent_crossover"
1526
+ source_attribution_output = (
1527
+ _TwoParentSourceAttributionOutput
1528
+ if is_two_parent_crossover
1529
+ else _SourceAttributionOutput
1530
+ )
1531
+ fields: dict[str, tuple[Any, Any]] = {
1532
+ "configuration": (
1533
+ candidate_model,
1534
+ Field(description=_CONFIGURATION_ROOT_DESCRIPTION),
1535
+ ),
1536
+ "design_rationale": (
1537
+ _Rationale,
1538
+ Field(description="Why this design should improve the stated objectives."),
1539
+ ),
1540
+ "intended_changes": (
1541
+ list[_Change],
1542
+ Field(
1543
+ default_factory=list,
1544
+ max_length=MAX_INTENDED_CHANGES,
1545
+ description=(
1546
+ _TWO_PARENT_INTENDED_CHANGES_DESCRIPTION
1547
+ if is_two_parent_crossover
1548
+ else _INTENDED_CHANGES_DESCRIPTION
1549
+ ),
1550
+ ),
1551
+ ),
1552
+ "source_attribution": (
1553
+ list[source_attribution_output],
1554
+ Field(
1555
+ default_factory=list,
1556
+ max_length=MAX_SOURCE_ATTRIBUTIONS,
1557
+ description=(
1558
+ _TWO_PARENT_SOURCE_ATTRIBUTION_DESCRIPTION
1559
+ if is_two_parent_crossover
1560
+ else _SOURCE_ATTRIBUTION_DESCRIPTION
1561
+ ),
1562
+ ),
1563
+ ),
1564
+ "claimed_insight_ids": (
1565
+ list[_Identifier],
1566
+ Field(default_factory=list, max_length=MAX_CLAIMED_INSIGHT_IDS),
1567
+ ),
1568
+ }
1569
+ # Strict three-way recombination is the only current operator with opaque
1570
+ # preservation receipts or conflict-resolution annotations. Omitting these
1571
+ # fields from mutation/crossover schemas prevents irrelevant, token-heavy
1572
+ # explanations from becoming an accidental second task.
1573
+ if operation not in {
1574
+ "typed_mutation",
1575
+ "two_parent_crossover",
1576
+ "three_way_recombination",
1577
+ "repair",
1578
+ }:
1579
+ fields["claimed_preservation_obligation_ids"] = (
1580
+ list[_Identifier],
1581
+ Field(default_factory=list, max_length=MAX_PRESERVATION_IDS),
1582
+ )
1583
+ fields["conflict_resolutions"] = (
1584
+ list[_ConflictResolutionOutput],
1585
+ Field(default_factory=list, max_length=MAX_CONFLICT_RESOLUTIONS),
1586
+ )
1587
+ elif operation == "three_way_recombination":
1588
+ fields["conflict_resolutions"] = (
1589
+ list[_ConflictResolutionOutput],
1590
+ Field(default_factory=list, max_length=MAX_CONFLICT_RESOLUTIONS),
1591
+ )
1592
+ return create_model(
1593
+ "CandidateProposal",
1594
+ __base__=_CompactCandidateSchemaBase,
1595
+ __module__=__name__,
1596
+ **fields,
1597
+ )
1598
+
1599
+
1600
+ def _atomic_mutation_proposal_type(
1601
+ candidate_model: type[BaseModel],
1602
+ operation: str,
1603
+ contract: AtomicMutationOutputContract,
1604
+ ) -> type[BaseModel]:
1605
+ if not isinstance(candidate_model, type) or not issubclass(
1606
+ candidate_model, BaseModel
1607
+ ):
1608
+ raise TypeError("candidate_model must be a Pydantic BaseModel subclass")
1609
+ if candidate_model is BaseModel or getattr(
1610
+ candidate_model, "__pydantic_root_model__", False
1611
+ ):
1612
+ raise TypeError("candidate_model must describe an object configuration")
1613
+ if getattr(candidate_model, "__parameters__", ()):
1614
+ raise TypeError("candidate_model must be a concrete Pydantic model")
1615
+ if operation != "typed_mutation":
1616
+ raise ValueError("atomic mutation output is restricted to typed_mutation")
1617
+ if type(contract) is not AtomicMutationOutputContract:
1618
+ raise TypeError("contract must be an exact AtomicMutationOutputContract")
1619
+ AtomicMutationOutputContract.__post_init__(contract)
1620
+
1621
+ path_text = _path_text(contract.editable_path)
1622
+ path_literal = Literal.__getitem__((path_text,))
1623
+ output_type = create_model(
1624
+ "AtomicMutationProposal",
1625
+ __base__=_CompactAtomicMutationSchemaBase,
1626
+ __module__=__name__,
1627
+ path=(path_literal, Field(description="The exact contracted JSON path.")),
1628
+ replacement=(
1629
+ _AtomicScalar,
1630
+ Field(description="The replacement scalar at the contracted path."),
1631
+ ),
1632
+ design_rationale=(
1633
+ _Rationale,
1634
+ Field(description="Why this one edit should improve the objectives."),
1635
+ ),
1636
+ claimed_insight_ids=(
1637
+ list[_Identifier],
1638
+ Field(default_factory=list, max_length=MAX_CLAIMED_INSIGHT_IDS),
1639
+ ),
1640
+ )
1641
+ output_type.expected_path_text = path_text
1642
+ leaf_schema = _atomic_leaf_schema(
1643
+ candidate_model,
1644
+ contract.editable_path,
1645
+ )
1646
+ current = value_at_path(
1647
+ contract.parent_configuration,
1648
+ contract.editable_path,
1649
+ )
1650
+ output_type.replacement_schema = _exclude_current_scalar(
1651
+ leaf_schema,
1652
+ current,
1653
+ )
1654
+ if contract.replacement_options:
1655
+ for option in contract.replacement_options:
1656
+ target = replace_existing_path(
1657
+ contract.parent_configuration,
1658
+ contract.editable_path,
1659
+ option,
1660
+ )
1661
+ validated = candidate_model.model_validate(
1662
+ thaw_json(target),
1663
+ strict=True,
1664
+ by_alias=False,
1665
+ by_name=True,
1666
+ )
1667
+ if not typed_json_equal(
1668
+ freeze_json(_configuration_dict(validated)),
1669
+ target,
1670
+ ):
1671
+ raise ValueError(
1672
+ "candidate validation changed an atomic replacement option"
1673
+ )
1674
+ output_type.replacement_schema = _restrict_scalar_options(
1675
+ output_type.replacement_schema,
1676
+ contract.replacement_options,
1677
+ )
1678
+ output_type.candidate_model = candidate_model
1679
+ output_type.output_contract = contract
1680
+ return output_type
1681
+
1682
+
1683
+ def _exact_parent_crossover_proposal_type(
1684
+ operation: str,
1685
+ contract: ExactParentCrossoverOutputContract,
1686
+ ) -> type[BaseModel]:
1687
+ if operation != "two_parent_crossover":
1688
+ raise ValueError(
1689
+ "exact parent crossover output is restricted to two_parent_crossover"
1690
+ )
1691
+ if type(contract) is not ExactParentCrossoverOutputContract:
1692
+ raise TypeError("contract must be an exact ExactParentCrossoverOutputContract")
1693
+ ExactParentCrossoverOutputContract.__post_init__(contract)
1694
+ output_type = create_model(
1695
+ "ExactParentCrossoverPlan",
1696
+ __base__=_CompactExactParentCrossoverSchemaBase,
1697
+ __module__=__name__,
1698
+ import_locus_ids=(
1699
+ list[_Identifier],
1700
+ Field(min_length=1, max_length=len(contract.locus_ids) - 1),
1701
+ ),
1702
+ claimed_insight_ids=(
1703
+ list[_Identifier],
1704
+ Field(
1705
+ default_factory=list,
1706
+ max_length=min(
1707
+ MAX_CLAIMED_INSIGHT_IDS,
1708
+ len(contract.claimable_insight_ids),
1709
+ ),
1710
+ ),
1711
+ ),
1712
+ )
1713
+ output_type.allowed_locus_ids = contract.locus_ids
1714
+ output_type.claimable_insight_ids = contract.claimable_insight_ids
1715
+ output_type.forbidden_import_locus_sets = contract.forbidden_import_locus_sets
1716
+ return output_type
1717
+
1718
+
1719
+ def _finite_variation_selection_type(
1720
+ candidate_model: type[BaseModel],
1721
+ operation: str,
1722
+ contract: FiniteVariationContract,
1723
+ ) -> type[BaseModel]:
1724
+ """Build one strict Literal-ID tool schema over prevalidated full children."""
1725
+
1726
+ if not isinstance(candidate_model, type) or not issubclass(
1727
+ candidate_model, BaseModel
1728
+ ):
1729
+ raise TypeError("candidate_model must be a Pydantic BaseModel subclass")
1730
+ if candidate_model is BaseModel or getattr(
1731
+ candidate_model, "__pydantic_root_model__", False
1732
+ ):
1733
+ raise TypeError("candidate_model must describe an object configuration")
1734
+ if getattr(candidate_model, "__parameters__", ()):
1735
+ raise TypeError("candidate_model must be a concrete Pydantic model")
1736
+ if type(operation) is not str or not operation.strip():
1737
+ raise ValueError("operation must be non-empty")
1738
+ validate_finite_variation_contract(contract)
1739
+
1740
+ for option in contract.options:
1741
+ validated = candidate_model.model_validate(
1742
+ thaw_json(option.child_configuration),
1743
+ strict=True,
1744
+ by_alias=False,
1745
+ by_name=True,
1746
+ )
1747
+ if not typed_json_equal(
1748
+ freeze_json(_configuration_dict(validated)),
1749
+ option.child_configuration,
1750
+ ):
1751
+ raise ValueError("candidate validation changed a finite variation child")
1752
+
1753
+ option_ids = tuple(option.option_id for option in contract.options)
1754
+ option_literal = Literal.__getitem__(option_ids)
1755
+ output_type = create_model(
1756
+ "FiniteVariationSelectionProposal",
1757
+ __config__=_STRICT_MODEL_CONFIG,
1758
+ __module__=__name__,
1759
+ option_id=(
1760
+ option_literal,
1761
+ Field(description="The selected immutable variation option ID."),
1762
+ ),
1763
+ design_rationale=(
1764
+ _Rationale,
1765
+ Field(description="Why this sealed option should improve the objectives."),
1766
+ ),
1767
+ claimed_insight_ids=(
1768
+ list[_Identifier],
1769
+ Field(default_factory=list, max_length=MAX_CLAIMED_INSIGHT_IDS),
1770
+ ),
1771
+ )
1772
+ return output_type
1773
+
1774
+
1775
+ def _reflection_output_type(
1776
+ max_insights: int,
1777
+ available_contrast_ids: tuple[str, ...] = (),
1778
+ insight_contract: ReflectionInsightContract | None = None,
1779
+ *,
1780
+ min_insights: int = 0,
1781
+ evidence_catalog: ReflectionEvidenceCatalog | None = None,
1782
+ ) -> type[BaseModel]:
1783
+ if type(max_insights) is not int or not 1 <= max_insights <= 16:
1784
+ raise ValueError("max_insights must lie in [1,16]")
1785
+ if type(min_insights) is not int or not 0 <= min_insights <= max_insights:
1786
+ raise ValueError("min_insights must lie in [0,max_insights]")
1787
+ if type(available_contrast_ids) is not tuple:
1788
+ raise TypeError("available_contrast_ids must be an exact tuple")
1789
+ if evidence_catalog is not None:
1790
+ if type(evidence_catalog) is not ReflectionEvidenceCatalog:
1791
+ raise TypeError(
1792
+ "evidence_catalog must be an exact ReflectionEvidenceCatalog or None"
1793
+ )
1794
+ ReflectionEvidenceCatalog.__post_init__(evidence_catalog)
1795
+ if evidence_catalog.contrast_ids != available_contrast_ids:
1796
+ raise ValueError(
1797
+ "evidence_catalog must bind the exact available_contrast_ids"
1798
+ )
1799
+ citation_values = (
1800
+ evidence_catalog.citation_keys
1801
+ if evidence_catalog is not None
1802
+ else available_contrast_ids
1803
+ )
1804
+ if citation_values:
1805
+ allowed_contrast_id = Literal.__getitem__(citation_values)
1806
+ contrast_field = (
1807
+ list[allowed_contrast_id],
1808
+ Field(
1809
+ min_length=1,
1810
+ max_length=min(
1811
+ MAX_EVIDENCE_CONTRAST_IDS,
1812
+ len(citation_values),
1813
+ ),
1814
+ ),
1815
+ )
1816
+ else:
1817
+ contrast_field = (
1818
+ list[_ContrastIdentifier],
1819
+ Field(max_length=0),
1820
+ )
1821
+ if insight_contract is None:
1822
+ insight_base = (
1823
+ _CatalogReflectionInsightOutput
1824
+ if evidence_catalog is not None
1825
+ else _ReflectionInsightOutput
1826
+ )
1827
+ insight_type = create_model(
1828
+ "ReflectionInsightOutput",
1829
+ __base__=insight_base,
1830
+ __module__=__name__,
1831
+ **{
1832
+ (
1833
+ "evidence_citation_keys"
1834
+ if evidence_catalog is not None
1835
+ else "evidence_contrast_ids"
1836
+ ): contrast_field
1837
+ },
1838
+ )
1839
+ else:
1840
+ if type(insight_contract) is not ReflectionInsightContract:
1841
+ raise TypeError(
1842
+ "insight_contract must be an exact ReflectionInsightContract"
1843
+ )
1844
+ ReflectionInsightContract.__post_init__(insight_contract)
1845
+ metric_id_literal = Literal.__getitem__(insight_contract.required_metric_ids)
1846
+ option_family_literal = Literal.__getitem__(
1847
+ insight_contract.allowed_option_families
1848
+ )
1849
+ metric_prediction_type = create_model(
1850
+ "MetricEffectPredictionOutput",
1851
+ __base__=_MetricEffectPredictionOutput,
1852
+ __module__=__name__,
1853
+ metric_id=(metric_id_literal, ...),
1854
+ )
1855
+ advanced_base = (
1856
+ _CatalogAdvancedReflectionInsightOutput
1857
+ if evidence_catalog is not None
1858
+ else _AdvancedReflectionInsightOutput
1859
+ )
1860
+ insight_type = create_model(
1861
+ "InterventionInsightOutput",
1862
+ __base__=advanced_base,
1863
+ __module__=__name__,
1864
+ **{
1865
+ (
1866
+ "evidence_citation_keys"
1867
+ if evidence_catalog is not None
1868
+ else "evidence_contrast_ids"
1869
+ ): contrast_field,
1870
+ },
1871
+ effect_predictions=(
1872
+ list[metric_prediction_type],
1873
+ Field(
1874
+ min_length=len(insight_contract.required_metric_ids),
1875
+ max_length=len(insight_contract.required_metric_ids),
1876
+ ),
1877
+ ),
1878
+ recommended_option_families=(
1879
+ list[option_family_literal],
1880
+ Field(
1881
+ min_length=1,
1882
+ max_length=len(insight_contract.allowed_option_families),
1883
+ ),
1884
+ ),
1885
+ **(
1886
+ {}
1887
+ if not insight_contract.allowed_option_ids
1888
+ else {
1889
+ "recommended_option_ids": (
1890
+ list[Literal.__getitem__(insight_contract.allowed_option_ids)],
1891
+ Field(
1892
+ min_length=1,
1893
+ max_length=len(insight_contract.allowed_option_ids),
1894
+ ),
1895
+ )
1896
+ }
1897
+ ),
1898
+ )
1899
+ insight_type.required_metric_ids = insight_contract.required_metric_ids
1900
+ insight_type.allowed_option_families = insight_contract.allowed_option_families
1901
+ insight_type.allowed_option_ids = insight_contract.allowed_option_ids
1902
+ insight_type.allowed_decision_paths = insight_contract.allowed_decision_paths
1903
+ insight_type.allowed_insight_kinds = tuple(
1904
+ value.value for value in insight_contract.allowed_insight_kinds
1905
+ )
1906
+ insight_type.allowed_consumer_scopes = tuple(
1907
+ value.value for value in insight_contract.allowed_consumer_scopes
1908
+ )
1909
+ insight_type.allowed_comparison_anchor_kinds = tuple(
1910
+ value.value for value in insight_contract.allowed_comparison_anchor_kinds
1911
+ )
1912
+ insight_type.allowed_factor_capabilities = (
1913
+ insight_contract.allowed_factor_capabilities
1914
+ )
1915
+ insight_type.allowed_source_role_ids = insight_contract.allowed_source_role_ids
1916
+ insight_type.semantic_v3 = insight_contract.is_semantic_v3
1917
+ insights_field = (
1918
+ Field(default_factory=list, max_length=max_insights)
1919
+ if min_insights == 0
1920
+ else Field(
1921
+ min_length=min_insights,
1922
+ max_length=max_insights,
1923
+ )
1924
+ )
1925
+ output_type = create_model(
1926
+ "ReflectionOutput",
1927
+ __base__=_CompactReflectionSchemaBase,
1928
+ __module__=__name__,
1929
+ insights=(
1930
+ list[insight_type],
1931
+ insights_field,
1932
+ ),
1933
+ )
1934
+ output_type.available_contrast_ids = available_contrast_ids
1935
+ output_type.available_evidence_citation_keys = (
1936
+ () if evidence_catalog is None else evidence_catalog.citation_keys
1937
+ )
1938
+ output_type.evidence_catalog_identity_sha256 = (
1939
+ None if evidence_catalog is None else evidence_catalog.catalog_identity_sha256
1940
+ )
1941
+ output_type.insight_contract = insight_contract
1942
+ output_type.min_insights = min_insights
1943
+ output_type.max_insights = max_insights
1944
+ return output_type
1945
+
1946
+
1947
+ def _validated_response(
1948
+ result: object,
1949
+ *,
1950
+ output_type: type[BaseModel],
1951
+ ) -> tuple[StructuredGenerationResponse[Any], int]:
1952
+ if type(result) is AttemptedStructuredGenerationResponse:
1953
+ AttemptedStructuredGenerationResponse.__post_init__(result)
1954
+ response = result.response
1955
+ attempt_count = result.attempt_count
1956
+ elif type(result) is StructuredGenerationResponse:
1957
+ response = result
1958
+ attempt_count = 1
1959
+ else:
1960
+ raise TypeError(
1961
+ "low-level runner must return StructuredGenerationResponse or "
1962
+ "AttemptedStructuredGenerationResponse"
1963
+ )
1964
+
1965
+ StructuredGenerationResponse.__post_init__(response)
1966
+ if type(response.value) is not output_type:
1967
+ raise TypeError(
1968
+ "low-level response value does not match its requested output type"
1969
+ )
1970
+ return response, attempt_count
1971
+
1972
+
1973
+ def _telemetry(
1974
+ response: StructuredGenerationResponse[Any],
1975
+ *,
1976
+ attempt_count: int,
1977
+ ) -> AgenticCallTelemetry:
1978
+ return AgenticCallTelemetry(
1979
+ requested_model=response.requested_model,
1980
+ resolved_model=response.resolved_model,
1981
+ resolved_provider=response.resolved_provider,
1982
+ provider_response_id=response.provider_response_id,
1983
+ finish_reason=response.finish_reason,
1984
+ input_tokens=response.input_tokens,
1985
+ output_tokens=response.output_tokens,
1986
+ reasoning_tokens=response.reasoning_tokens,
1987
+ cache_read_tokens=response.cache_read_tokens,
1988
+ cache_write_tokens=response.cache_write_tokens,
1989
+ cost_usd=response.cost_usd,
1990
+ latency_ns=response.latency_ns,
1991
+ attempt_count=attempt_count,
1992
+ )
1993
+
1994
+
1995
+ def _configuration_dict(configuration: BaseModel) -> dict[str, Any]:
1996
+ # Invoke BaseModel's implementation directly so a candidate cannot replace
1997
+ # this trust-boundary operation with an overriding method.
1998
+ value = BaseModel.model_dump(
1999
+ configuration,
2000
+ mode="python",
2001
+ by_alias=False,
2002
+ exclude_unset=False,
2003
+ exclude_defaults=False,
2004
+ exclude_none=False,
2005
+ exclude_computed_fields=True,
2006
+ round_trip=True,
2007
+ warnings="error",
2008
+ fallback=None,
2009
+ serialize_as_any=False,
2010
+ polymorphic_serialization=False,
2011
+ )
2012
+ if type(value) is not dict: # pragma: no cover - guarded by model admission.
2013
+ raise TypeError("candidate configuration must serialize to an exact dict")
2014
+ return value
2015
+
2016
+
2017
+ class PydanticAIAgenticGenerator:
2018
+ """Map strict Pydantic outputs from an injected async runner into port values."""
2019
+
2020
+ def __init__(self, generate_once: LowLevelRunner) -> None:
2021
+ if not callable(generate_once):
2022
+ raise TypeError("generate_once must be callable")
2023
+ self._generate_once = generate_once
2024
+
2025
+ async def propose(
2026
+ self,
2027
+ request: VariationGenerationRequest,
2028
+ ) -> VariationGenerationResult:
2029
+ if type(request) is not VariationGenerationRequest:
2030
+ raise TypeError("request must be an exact VariationGenerationRequest")
2031
+ VariationGenerationRequest.__post_init__(request)
2032
+
2033
+ atomic_contract = request.atomic_mutation_contract
2034
+ finite_contract = request.finite_variation_contract
2035
+ crossover_contract = request.exact_parent_crossover_contract
2036
+ if (
2037
+ atomic_contract is None
2038
+ and finite_contract is None
2039
+ and crossover_contract is None
2040
+ ):
2041
+ output_type = _candidate_proposal_type(
2042
+ request.candidate_model,
2043
+ request.operation,
2044
+ )
2045
+ output_tool_name = CANDIDATE_PROPOSAL_TOOL_NAME
2046
+ elif atomic_contract is not None:
2047
+ output_type = _atomic_mutation_proposal_type(
2048
+ request.candidate_model,
2049
+ request.operation,
2050
+ atomic_contract,
2051
+ )
2052
+ output_tool_name = ATOMIC_MUTATION_TOOL_NAME
2053
+ elif crossover_contract is not None:
2054
+ output_type = _exact_parent_crossover_proposal_type(
2055
+ request.operation,
2056
+ crossover_contract,
2057
+ )
2058
+ output_tool_name = EXACT_PARENT_CROSSOVER_TOOL_NAME
2059
+ else:
2060
+ assert finite_contract is not None
2061
+ output_type = _finite_variation_selection_type(
2062
+ request.candidate_model,
2063
+ request.operation,
2064
+ finite_contract,
2065
+ )
2066
+ output_tool_name = FINITE_VARIATION_SELECTION_TOOL_NAME
2067
+ low_level_request = StructuredGenerationRequest(
2068
+ call_id=request.call_id,
2069
+ operation=request.operation,
2070
+ prompt=request.prompt,
2071
+ output_type=output_type,
2072
+ output_tool_name=output_tool_name,
2073
+ max_output_tokens=request.max_output_tokens,
2074
+ temperature=request.temperature,
2075
+ prompt_lineage=identity_prompt_lineage(request.prompt),
2076
+ )
2077
+ # Deliberately no exception handler: scheduler/provider errors retain
2078
+ # their original type, identity, and retry classification.
2079
+ low_level_result = await self._generate_once(low_level_request)
2080
+ response, attempt_count = _validated_response(
2081
+ low_level_result,
2082
+ output_type=output_type,
2083
+ )
2084
+ proposal = cast(Any, response.value)
2085
+ if atomic_contract is not None:
2086
+ replacement = freeze_json(proposal.replacement)
2087
+ draft: (
2088
+ CandidateDraft
2089
+ | AtomicMutationDraft
2090
+ | FiniteVariationSelectionDraft
2091
+ | ExactParentCrossoverDraft
2092
+ ) = AtomicMutationDraft(
2093
+ path=atomic_contract.editable_path,
2094
+ replacement=replacement,
2095
+ design_rationale=proposal.design_rationale,
2096
+ claimed_insight_ids=tuple(proposal.claimed_insight_ids),
2097
+ )
2098
+ elif crossover_contract is not None:
2099
+ draft = ExactParentCrossoverDraft(
2100
+ contract_identity_sha256=(crossover_contract.contract_identity_sha256),
2101
+ import_locus_ids=tuple(sorted(proposal.import_locus_ids)),
2102
+ claimed_insight_ids=tuple(proposal.claimed_insight_ids),
2103
+ )
2104
+ elif finite_contract is not None:
2105
+ option = finite_contract.resolve(proposal.option_id)
2106
+ draft = FiniteVariationSelectionDraft(
2107
+ option_id=option.option_id,
2108
+ option_identity_sha256=option.identity_sha256,
2109
+ contract_identity_sha256=finite_contract.identity_sha256,
2110
+ design_rationale=proposal.design_rationale,
2111
+ claimed_insight_ids=tuple(proposal.claimed_insight_ids),
2112
+ )
2113
+ else:
2114
+ draft = CandidateDraft(
2115
+ configuration=_configuration_dict(proposal.configuration),
2116
+ design_rationale=proposal.design_rationale,
2117
+ intended_changes=tuple(proposal.intended_changes),
2118
+ source_attribution=tuple(
2119
+ SourceAttribution(path=item.path, source=item.source)
2120
+ for item in proposal.source_attribution
2121
+ ),
2122
+ claimed_insight_ids=tuple(proposal.claimed_insight_ids),
2123
+ claimed_preservation_obligation_ids=tuple(
2124
+ getattr(proposal, "claimed_preservation_obligation_ids", ())
2125
+ ),
2126
+ conflict_resolutions=tuple(
2127
+ ConflictResolutionDraft(
2128
+ relation_id=item.relation_id,
2129
+ choice=item.choice,
2130
+ explanation=item.explanation,
2131
+ )
2132
+ for item in getattr(proposal, "conflict_resolutions", ())
2133
+ ),
2134
+ )
2135
+ return VariationGenerationResult(
2136
+ draft=draft,
2137
+ telemetry=_telemetry(response, attempt_count=attempt_count),
2138
+ )
2139
+
2140
+ async def reflect(
2141
+ self,
2142
+ request: ReflectionGenerationRequest,
2143
+ ) -> ReflectionGenerationResult:
2144
+ if type(request) is not ReflectionGenerationRequest:
2145
+ raise TypeError("request must be an exact ReflectionGenerationRequest")
2146
+ ReflectionGenerationRequest.__post_init__(request)
2147
+
2148
+ output_type = _reflection_output_type(
2149
+ request.max_insights,
2150
+ request.available_contrast_ids,
2151
+ request.insight_contract,
2152
+ min_insights=request.min_insights,
2153
+ evidence_catalog=request.evidence_catalog,
2154
+ )
2155
+ low_level_request = StructuredGenerationRequest(
2156
+ call_id=request.call_id,
2157
+ operation=request.operation,
2158
+ prompt=render_reflection_prompt(
2159
+ request.prompt,
2160
+ request.evidence_catalog,
2161
+ insight_contract=request.insight_contract,
2162
+ ),
2163
+ output_type=output_type,
2164
+ output_tool_name=REFLECTION_TOOL_NAME,
2165
+ max_output_tokens=request.max_output_tokens,
2166
+ temperature=request.temperature,
2167
+ prompt_lineage=StructuredPromptLineage(
2168
+ semantic_prompt_sha256=hashlib.sha256(
2169
+ request.prompt.encode("utf-8", errors="strict")
2170
+ ).hexdigest(),
2171
+ renderer_id=REFLECTION_PROMPT_RENDERER_ID,
2172
+ renderer_revision=(
2173
+ REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION
2174
+ if request.insight_contract is not None
2175
+ and request.insight_contract.is_semantic_v3
2176
+ else (
2177
+ REFLECTION_PROMPT_RENDERER_REVISION
2178
+ if request.evidence_catalog is None
2179
+ else REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION
2180
+ )
2181
+ ),
2182
+ renderer_definition_sha256=(
2183
+ REFLECTION_SEMANTIC_PROMPT_RENDERER_DEFINITION_SHA256
2184
+ if request.insight_contract is not None
2185
+ and request.insight_contract.is_semantic_v3
2186
+ else (
2187
+ REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256
2188
+ if request.evidence_catalog is None
2189
+ else (
2190
+ REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256
2191
+ )
2192
+ )
2193
+ ),
2194
+ ),
2195
+ )
2196
+ low_level_result = await self._generate_once(low_level_request)
2197
+ response, attempt_count = _validated_response(
2198
+ low_level_result,
2199
+ output_type=output_type,
2200
+ )
2201
+ reflection = cast(Any, response.value)
2202
+
2203
+ def resolved_evidence_ids(item: Any) -> tuple[str, ...]:
2204
+ catalog = request.evidence_catalog
2205
+ if catalog is None:
2206
+ return tuple(sorted(item.evidence_contrast_ids))
2207
+ citation_keys = tuple(item.evidence_citation_keys)
2208
+ return catalog.resolve_citation_keys(citation_keys)
2209
+
2210
+ insights = tuple(
2211
+ InsightDraft(
2212
+ claim=item.claim,
2213
+ trigger=item.trigger,
2214
+ mechanism=item.mechanism,
2215
+ # ``affected_paths`` is a set-like semantic field. Provider
2216
+ # order is not meaningful, while every downstream evidence
2217
+ # and audit identity requires canonical lexical order. Keep
2218
+ # duplicates intact so the semantic validator can reject them
2219
+ # instead of silently repairing invalid content.
2220
+ affected_paths=tuple(sorted(item.affected_paths)),
2221
+ evidence_summary=item.evidence_summary,
2222
+ confidence=float(item.confidence),
2223
+ evidence_contrast_ids=resolved_evidence_ids(item),
2224
+ effect_predictions=tuple(
2225
+ sorted(
2226
+ (
2227
+ MetricEffectPrediction(
2228
+ metric_id=prediction.metric_id,
2229
+ direction=MetricEffectDirection(prediction.direction),
2230
+ comparison_anchor=(
2231
+ None
2232
+ if prediction.comparison_anchor is None
2233
+ else MetricComparisonAnchor(
2234
+ kind=MetricComparisonAnchorKind(
2235
+ prediction.comparison_anchor.kind
2236
+ ),
2237
+ source_role_id=(
2238
+ prediction.comparison_anchor.source_role_id
2239
+ ),
2240
+ )
2241
+ ),
2242
+ )
2243
+ for prediction in getattr(item, "effect_predictions", ())
2244
+ ),
2245
+ key=lambda prediction: prediction.metric_id,
2246
+ )
2247
+ ),
2248
+ recommended_option_families=tuple(
2249
+ sorted(getattr(item, "recommended_option_families", ()))
2250
+ ),
2251
+ recommended_option_ids=tuple(
2252
+ sorted(getattr(item, "recommended_option_ids", ()))
2253
+ ),
2254
+ action_template=getattr(item, "action_template", None),
2255
+ falsification_condition=getattr(item, "falsification_condition", None),
2256
+ insight_kind=(
2257
+ None
2258
+ if getattr(item, "insight_kind", None) is None
2259
+ else ReflectionInsightKind(item.insight_kind)
2260
+ ),
2261
+ consumer_scopes=tuple(
2262
+ sorted(
2263
+ (
2264
+ ReflectionConsumerScope(value)
2265
+ for value in getattr(item, "consumer_scopes", ())
2266
+ ),
2267
+ key=lambda value: value.value,
2268
+ )
2269
+ ),
2270
+ factor_capabilities=tuple(
2271
+ sorted(getattr(item, "factor_capabilities", ()))
2272
+ ),
2273
+ )
2274
+ for item in reflection.insights
2275
+ )
2276
+ result = ReflectionGenerationResult(
2277
+ insights=insights,
2278
+ telemetry=_telemetry(response, attempt_count=attempt_count),
2279
+ evidence_catalog_identity_sha256=(
2280
+ None
2281
+ if request.evidence_catalog is None
2282
+ else request.evidence_catalog.catalog_identity_sha256
2283
+ ),
2284
+ )
2285
+ validate_reflection_evidence_catalog_result(request, result)
2286
+ return result
2287
+
2288
+
2289
+ __all__ = [
2290
+ "ATOMIC_MUTATION_TOOL_NAME",
2291
+ "AttemptedStructuredGenerationResponse",
2292
+ "CANDIDATE_PROPOSAL_TOOL_NAME",
2293
+ "FINITE_VARIATION_SELECTION_TOOL_NAME",
2294
+ "LowLevelRunner",
2295
+ "PydanticAIAgenticGenerator",
2296
+ "REFLECTION_OUTPUT_CONTRACT_NOTE",
2297
+ "REFLECTION_OUTPUT_CONTRACT_NOTE_SHA256",
2298
+ "REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256",
2299
+ "REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION",
2300
+ "REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256",
2301
+ "REFLECTION_PROMPT_RENDERER_ID",
2302
+ "REFLECTION_PROMPT_RENDERER_REVISION",
2303
+ "REFLECTION_SEMANTIC_PROMPT_RENDERER_DEFINITION_SHA256",
2304
+ "REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION",
2305
+ "REFLECTION_TOOL_NAME",
2306
+ "REFLECTION_WIRE_CONTRACT_REVISION",
2307
+ "render_reflection_prompt",
2308
+ ]