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,1568 @@
1
+ """Provider-neutral all-option action forecasts with trusted resolution.
2
+
3
+ Models author only numeric forecast drafts and opaque citations. Trusted code
4
+ binds a complete draft batch back to one optimization-semantics snapshot, one
5
+ parent-bound finite-action contract, and authenticated prompt-visible card
6
+ views. Partial batches are deliberately not representable after resolution.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hashlib
12
+ import json
13
+ import math
14
+ import re
15
+ from dataclasses import dataclass
16
+ from enum import Enum
17
+ from typing import Protocol, runtime_checkable
18
+
19
+ from agent_evolve.core.action_semantics import ActionSpaceSemantics
20
+ from agent_evolve.core.optimization_semantics import OptimizationSemantics
21
+ from agent_evolve.domain.finite_variation import (
22
+ FiniteActionEvidenceBinding,
23
+ FiniteVariationContract,
24
+ validate_finite_variation_contract,
25
+ )
26
+ from agent_evolve.domain.ids import LLMCallId
27
+ from agent_evolve.domain.patch import require_sha256
28
+ from agent_evolve.domain.typed_json import FrozenJsonObject, freeze_json, typed_json_sha256
29
+ from agent_evolve.ports.agentic_generator import AgenticCallTelemetry
30
+ from agent_evolve.ports.portfolio_selection import (
31
+ PortfolioCard,
32
+ PortfolioCardSourceRegistry,
33
+ PortfolioExperimentalViewReceipt,
34
+ portfolio_card_action_evidence_sha256,
35
+ portfolio_card_snapshot_sha256,
36
+ validate_portfolio_experimental_view,
37
+ )
38
+ from agent_evolve.ports.structured_generator import MAX_OUTPUT_TOKENS
39
+
40
+
41
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
42
+ _OPTION_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,255}$")
43
+ _METRIC_ID = re.compile(r"^[a-z][a-z0-9_.:-]{0,191}$")
44
+ _REQUEST_DOMAIN = b"agent-evolve:action-forecast-request:v2\x00"
45
+ _BATCH_DOMAIN = b"agent-evolve:resolved-action-forecast-batch:v2\x00"
46
+ _PARTITION_POLICY_DOMAIN = b"agent-evolve:action-forecast-partition-policy:v1\x00"
47
+ _BLOCK_SPEC_DOMAIN = b"agent-evolve:action-forecast-block-spec:v1\x00"
48
+ _PARTITION_LAYOUT_DOMAIN = b"agent-evolve:action-forecast-partition-layout:v1\x00"
49
+ _BLOCK_REQUEST_DOMAIN = b"agent-evolve:action-forecast-block-request:v1\x00"
50
+ _RESOLVED_BLOCK_DOMAIN = b"agent-evolve:resolved-action-forecast-block:v1\x00"
51
+
52
+
53
+ def _canonical_json(value: object) -> bytes:
54
+ return json.dumps(
55
+ value,
56
+ allow_nan=False,
57
+ ensure_ascii=True,
58
+ separators=(",", ":"),
59
+ sort_keys=True,
60
+ ).encode("ascii")
61
+
62
+
63
+ def _hash(domain: bytes, value: object) -> str:
64
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
65
+
66
+
67
+ def _finite_float(value: object, name: str) -> float:
68
+ if type(value) is not float or not math.isfinite(value):
69
+ raise TypeError(f"{name} must be a finite canonical float")
70
+ return value
71
+
72
+
73
+ def _canonical_metric_ids(values: tuple[str, ...], name: str) -> None:
74
+ if type(values) is not tuple or any(
75
+ type(value) is not str or _METRIC_ID.fullmatch(value) is None
76
+ for value in values
77
+ ):
78
+ raise TypeError(f"{name} must be an exact tuple of metric identifiers")
79
+ if not values:
80
+ raise ValueError(f"{name} must be non-empty")
81
+ if values != tuple(sorted(set(values))):
82
+ raise ValueError(f"{name} must be unique and canonical")
83
+
84
+
85
+ @dataclass(frozen=True, slots=True)
86
+ class ParentMetricValue:
87
+ """One finite parent value in the optimization semantics' metric space."""
88
+
89
+ metric_id: str
90
+ value: float
91
+
92
+ def __post_init__(self) -> None:
93
+ _canonical_metric_ids((self.metric_id,), "metric_id")
94
+ _finite_float(self.value, "value")
95
+
96
+ def to_record(self) -> dict[str, object]:
97
+ self.__post_init__()
98
+ return {"metric_id": self.metric_id, "value_hex": self.value.hex()}
99
+
100
+
101
+ @dataclass(frozen=True, slots=True)
102
+ class MetricForecastScale:
103
+ """Explicit positive scale used to interpret one metric's delta forecast."""
104
+
105
+ metric_id: str
106
+ delta_scale: float
107
+ definition_sha256: str
108
+
109
+ def __post_init__(self) -> None:
110
+ _canonical_metric_ids((self.metric_id,), "metric_id")
111
+ _finite_float(self.delta_scale, "delta_scale")
112
+ if self.delta_scale <= 0.0:
113
+ raise ValueError("delta_scale must be strictly positive")
114
+ require_sha256(self.definition_sha256, "definition_sha256")
115
+
116
+ def to_record(self) -> dict[str, object]:
117
+ self.__post_init__()
118
+ return {
119
+ "metric_id": self.metric_id,
120
+ "delta_scale_hex": self.delta_scale.hex(),
121
+ "definition_sha256": self.definition_sha256,
122
+ }
123
+
124
+
125
+ @dataclass(frozen=True, slots=True)
126
+ class ActionEvidenceCitation:
127
+ """Model-authored pointer to one prompt-visible card/action binding."""
128
+
129
+ card_key: str
130
+ action_binding_identity_sha256: str
131
+
132
+ def __post_init__(self) -> None:
133
+ if type(self.card_key) is not str or _TOKEN.fullmatch(self.card_key) is None:
134
+ raise ValueError("card_key must use the closed lowercase token grammar")
135
+ require_sha256(
136
+ self.action_binding_identity_sha256,
137
+ "action_binding_identity_sha256",
138
+ )
139
+
140
+ def to_record(self) -> dict[str, object]:
141
+ self.__post_init__()
142
+ return {
143
+ "card_key": self.card_key,
144
+ "action_binding_identity_sha256": (
145
+ self.action_binding_identity_sha256
146
+ ),
147
+ }
148
+
149
+
150
+ class ActionForecastEvidenceMode(str, Enum):
151
+ """Request-scoped evidence/citation treatment contract."""
152
+
153
+ GROUNDED = "grounded"
154
+ CATALOG_ONLY = "catalog_only"
155
+
156
+
157
+ @dataclass(frozen=True, slots=True)
158
+ class ActionMetricForecast:
159
+ """Calibratable delta quantiles for one required metric."""
160
+
161
+ metric_id: str
162
+ p10_delta: float
163
+ p50_delta: float
164
+ p90_delta: float
165
+ confidence: float
166
+ citations: tuple[ActionEvidenceCitation, ...]
167
+
168
+ def __post_init__(self) -> None:
169
+ _canonical_metric_ids((self.metric_id,), "metric_id")
170
+ for name in ("p10_delta", "p50_delta", "p90_delta", "confidence"):
171
+ _finite_float(getattr(self, name), name)
172
+ if not self.p10_delta <= self.p50_delta <= self.p90_delta:
173
+ raise ValueError("delta quantiles must satisfy p10 <= p50 <= p90")
174
+ if not 0.0 <= self.confidence <= 1.0:
175
+ raise ValueError("confidence must lie in [0,1]")
176
+ if type(self.citations) is not tuple or any(
177
+ type(value) is not ActionEvidenceCitation for value in self.citations
178
+ ):
179
+ raise TypeError("citations must be an exact tuple of action citations")
180
+ for citation in self.citations:
181
+ citation.__post_init__()
182
+ keys = tuple(
183
+ (value.card_key, value.action_binding_identity_sha256)
184
+ for value in self.citations
185
+ )
186
+ if keys != tuple(sorted(set(keys))):
187
+ raise ValueError("citations must be unique and canonical")
188
+
189
+ def to_record(self) -> dict[str, object]:
190
+ self.__post_init__()
191
+ return {
192
+ "metric_id": self.metric_id,
193
+ "p10_delta_hex": self.p10_delta.hex(),
194
+ "p50_delta_hex": self.p50_delta.hex(),
195
+ "p90_delta_hex": self.p90_delta.hex(),
196
+ "confidence_hex": self.confidence.hex(),
197
+ "citations": [value.to_record() for value in self.citations],
198
+ }
199
+
200
+
201
+ @dataclass(frozen=True, slots=True)
202
+ class ActionForecastDraft:
203
+ """Untrusted structured output for exactly one opaque finite option."""
204
+
205
+ option_id: str
206
+ probability_valid: float
207
+ metric_forecasts: tuple[ActionMetricForecast, ...]
208
+
209
+ def __post_init__(self) -> None:
210
+ if type(self.option_id) is not str or _OPTION_ID.fullmatch(self.option_id) is None:
211
+ raise ValueError("option_id must use the closed finite-option grammar")
212
+ _finite_float(self.probability_valid, "probability_valid")
213
+ if not 0.0 <= self.probability_valid <= 1.0:
214
+ raise ValueError("probability_valid must lie in [0,1]")
215
+ if type(self.metric_forecasts) is not tuple or not self.metric_forecasts or any(
216
+ type(value) is not ActionMetricForecast
217
+ for value in self.metric_forecasts
218
+ ):
219
+ raise ValueError(
220
+ "metric_forecasts must be a non-empty exact tuple of forecasts"
221
+ )
222
+ for value in self.metric_forecasts:
223
+ value.__post_init__()
224
+ metric_ids = tuple(value.metric_id for value in self.metric_forecasts)
225
+ _canonical_metric_ids(metric_ids, "metric_forecasts")
226
+
227
+
228
+ def _prompt_action_bindings(
229
+ card: PortfolioCard,
230
+ ) -> tuple[FiniteActionEvidenceBinding, ...]:
231
+ """Read only the authenticated prompt-visible action-evidence projection.
232
+
233
+ Source provenance is intentionally not consulted here: experimental card
234
+ views may permute or redact presented action evidence while retaining their
235
+ immutable source binding. ``PortfolioCard.__post_init__`` authenticates
236
+ whichever projection ``prompt_record`` exposes.
237
+ """
238
+
239
+ card.__post_init__()
240
+ return card.finite_action_evidence
241
+
242
+
243
+ def _validate_admitted_card_views(
244
+ cards: tuple[PortfolioCard, ...],
245
+ source_registry: PortfolioCardSourceRegistry,
246
+ ) -> None:
247
+ """Apply the same closed source/donor checks as scientific selection."""
248
+
249
+ if type(source_registry) is not PortfolioCardSourceRegistry:
250
+ raise TypeError("source_registry must be trusted PortfolioCardSourceRegistry")
251
+ source_registry.__post_init__()
252
+ references = tuple(card.reference for card in cards)
253
+ if len(set(references)) != len(references):
254
+ raise ValueError("cards cannot repeat an exact insight reference")
255
+ source_bindings = {
256
+ card.source_binding.binding_sha256: card.source_binding
257
+ for card in cards
258
+ if card.source_binding is not None
259
+ }
260
+ if len(source_bindings) != len(cards):
261
+ raise ValueError("forecast requests require source-bound cards only")
262
+ admitted = {
263
+ binding.binding_sha256: binding
264
+ for binding in source_registry.source_bindings
265
+ }
266
+ if admitted.keys() != source_bindings.keys():
267
+ raise ValueError("source registry differs from the request card source set")
268
+ for binding_sha256, binding in source_bindings.items():
269
+ if admitted[binding_sha256] != binding:
270
+ raise ValueError("source registry binding differs from the request card")
271
+ for card in cards:
272
+ receipt = card.derived_view_receipt
273
+ if receipt is None:
274
+ continue
275
+ evidence_source_sha256 = receipt.evidence_source_binding_sha256
276
+ if evidence_source_sha256 is not None:
277
+ evidence_source = source_bindings.get(evidence_source_sha256)
278
+ if evidence_source is None:
279
+ raise ValueError("derived evidence donor is outside the request")
280
+ if (
281
+ receipt.derived_evidence_sha256
282
+ != evidence_source.source_evidence_sha256
283
+ ):
284
+ raise ValueError("derived evidence differs from its admitted donor")
285
+ score_source_sha256 = receipt.score_source_binding_sha256
286
+ if score_source_sha256 is not None:
287
+ score_source = source_bindings.get(score_source_sha256)
288
+ if score_source is None:
289
+ raise ValueError("derived score donor is outside the request")
290
+ if (
291
+ receipt.derived_score_state_sha256
292
+ != score_source.source_score_state_sha256
293
+ ):
294
+ raise ValueError("derived score state differs from its admitted donor")
295
+ action_source_sha256 = receipt.action_evidence_source_binding_sha256
296
+ if action_source_sha256 is not None:
297
+ action_source = source_bindings.get(action_source_sha256)
298
+ if action_source is None:
299
+ raise ValueError("derived action-evidence donor is outside the request")
300
+ if (
301
+ receipt.derived_action_evidence_sha256
302
+ != portfolio_card_action_evidence_sha256(
303
+ action_source.finite_action_evidence
304
+ )
305
+ ):
306
+ raise ValueError(
307
+ "derived action evidence differs from its admitted donor"
308
+ )
309
+
310
+
311
+ @dataclass(frozen=True, slots=True)
312
+ class ActionForecastRequest:
313
+ """One logical call that must forecast every option and required metric."""
314
+
315
+ call_id: LLMCallId
316
+ operation: str
317
+ instruction: str
318
+ context: FrozenJsonObject
319
+ optimization_semantics: OptimizationSemantics
320
+ action_semantics: ActionSpaceSemantics
321
+ finite_variation_contract: FiniteVariationContract
322
+ cards: tuple[PortfolioCard, ...]
323
+ source_registry: PortfolioCardSourceRegistry | None
324
+ evidence_mode: ActionForecastEvidenceMode
325
+ experimental_view_receipt: PortfolioExperimentalViewReceipt | None
326
+ parent_metric_values: tuple[ParentMetricValue, ...]
327
+ metric_scales: tuple[MetricForecastScale, ...]
328
+ max_output_tokens: int = MAX_OUTPUT_TOKENS
329
+ temperature: float | None = None
330
+
331
+ def __post_init__(self) -> None:
332
+ if type(self.call_id) is not LLMCallId:
333
+ raise TypeError("call_id must be an exact LLMCallId")
334
+ LLMCallId.__post_init__(self.call_id)
335
+ if type(self.operation) is not str or _TOKEN.fullmatch(self.operation) is None:
336
+ raise ValueError("operation must use the closed lowercase token grammar")
337
+ if (
338
+ type(self.instruction) is not str
339
+ or not self.instruction.strip()
340
+ or self.instruction != self.instruction.strip()
341
+ ):
342
+ raise ValueError("instruction must be canonical non-empty text")
343
+ if type(self.context) is not FrozenJsonObject:
344
+ raise TypeError("context must be an exact FrozenJsonObject")
345
+ if freeze_json(self.context) is not self.context:
346
+ raise TypeError("context must already be frozen typed JSON")
347
+ if type(self.optimization_semantics) is not OptimizationSemantics:
348
+ raise TypeError(
349
+ "optimization_semantics must be exact OptimizationSemantics"
350
+ )
351
+ OptimizationSemantics.__post_init__(self.optimization_semantics)
352
+ validate_finite_variation_contract(self.finite_variation_contract)
353
+ if type(self.action_semantics) is not ActionSpaceSemantics:
354
+ raise TypeError("action_semantics must be exact ActionSpaceSemantics")
355
+ ActionSpaceSemantics.__post_init__(self.action_semantics)
356
+ self.action_semantics.validate_contract_binding(
357
+ (
358
+ self.finite_variation_contract.catalog_id,
359
+ self.finite_variation_contract.catalog_version,
360
+ self.finite_variation_contract.catalog_definition_sha256,
361
+ ),
362
+ tuple(
363
+ option.family
364
+ for option in self.finite_variation_contract.options
365
+ ),
366
+ )
367
+ if type(self.cards) is not tuple or any(
368
+ type(card) is not PortfolioCard for card in self.cards
369
+ ):
370
+ raise TypeError("cards must be an exact tuple of PortfolioCard")
371
+ for card in self.cards:
372
+ card.__post_init__()
373
+ card_keys = tuple(card.card_key for card in self.cards)
374
+ if card_keys != tuple(sorted(set(card_keys))):
375
+ raise ValueError("cards must use unique canonical card_key order")
376
+ if type(self.evidence_mode) is not ActionForecastEvidenceMode:
377
+ raise TypeError("evidence_mode must be exact ActionForecastEvidenceMode")
378
+ if self.evidence_mode is ActionForecastEvidenceMode.GROUNDED:
379
+ if not self.cards:
380
+ raise ValueError("grounded forecasts require admitted evidence cards")
381
+ if type(self.source_registry) is not PortfolioCardSourceRegistry:
382
+ raise ValueError("grounded forecasts require a trusted source registry")
383
+ _validate_admitted_card_views(self.cards, self.source_registry)
384
+ if type(self.experimental_view_receipt) is not PortfolioExperimentalViewReceipt:
385
+ raise ValueError(
386
+ "grounded forecasts require a scientific experimental-view receipt"
387
+ )
388
+ validate_portfolio_experimental_view(
389
+ cards=self.cards,
390
+ finite_variation_contract=self.finite_variation_contract,
391
+ source_registry=self.source_registry,
392
+ receipt=self.experimental_view_receipt,
393
+ )
394
+ else:
395
+ if (
396
+ self.cards
397
+ or self.source_registry is not None
398
+ or self.experimental_view_receipt is not None
399
+ ):
400
+ raise ValueError(
401
+ "catalog-only forecasts forbid cards, source admission, and "
402
+ "experimental-view receipts"
403
+ )
404
+ if type(self.parent_metric_values) is not tuple or any(
405
+ type(value) is not ParentMetricValue
406
+ for value in self.parent_metric_values
407
+ ):
408
+ raise TypeError("parent_metric_values must contain exact values")
409
+ if type(self.metric_scales) is not tuple or any(
410
+ type(value) is not MetricForecastScale for value in self.metric_scales
411
+ ):
412
+ raise TypeError("metric_scales must contain exact values")
413
+ for value in self.parent_metric_values:
414
+ value.__post_init__()
415
+ for value in self.metric_scales:
416
+ value.__post_init__()
417
+ parent_ids = tuple(value.metric_id for value in self.parent_metric_values)
418
+ scale_ids = tuple(value.metric_id for value in self.metric_scales)
419
+ _canonical_metric_ids(parent_ids, "parent_metric_values")
420
+ _canonical_metric_ids(scale_ids, "metric_scales")
421
+ if parent_ids != scale_ids:
422
+ raise ValueError("parent metric values and scales must cover identical metrics")
423
+ semantic_ids = {metric.metric_id for metric in self.optimization_semantics.metrics}
424
+ if not set(parent_ids).issubset(semantic_ids):
425
+ raise ValueError("forecast metrics escape the optimization semantics")
426
+ if type(self.max_output_tokens) is not int or not (
427
+ 1 <= self.max_output_tokens <= MAX_OUTPUT_TOKENS
428
+ ):
429
+ raise ValueError(
430
+ f"max_output_tokens must lie in [1, {MAX_OUTPUT_TOKENS}]"
431
+ )
432
+ if self.temperature is not None and (
433
+ isinstance(self.temperature, bool)
434
+ or not isinstance(self.temperature, (int, float))
435
+ or not math.isfinite(float(self.temperature))
436
+ or not 0.0 <= float(self.temperature) <= 2.0
437
+ ):
438
+ raise ValueError("temperature must be finite in [0,2] or None")
439
+
440
+ @property
441
+ def required_metric_ids(self) -> tuple[str, ...]:
442
+ self.__post_init__()
443
+ return tuple(value.metric_id for value in self.parent_metric_values)
444
+
445
+ @property
446
+ def context_sha256(self) -> str:
447
+ self.__post_init__()
448
+ return typed_json_sha256(self.context)
449
+
450
+ @property
451
+ def card_snapshot_sha256(self) -> str:
452
+ self.__post_init__()
453
+ return portfolio_card_snapshot_sha256(self.cards)
454
+
455
+ def to_record(self) -> dict[str, object]:
456
+ self.__post_init__()
457
+ return {
458
+ "schema_version": 2,
459
+ "call_id": self.call_id.value,
460
+ "operation": self.operation,
461
+ "instruction_sha256": hashlib.sha256(
462
+ self.instruction.encode("utf-8", errors="strict")
463
+ ).hexdigest(),
464
+ "context_sha256": self.context_sha256,
465
+ "optimization_semantics": {
466
+ "semantics_id": self.optimization_semantics.semantics_id,
467
+ "semantics_version": self.optimization_semantics.semantics_version,
468
+ "definition_sha256": self.optimization_semantics.definition_sha256,
469
+ },
470
+ "action_semantics": {
471
+ "semantics_id": self.action_semantics.semantics_id,
472
+ "semantics_version": self.action_semantics.semantics_version,
473
+ "definition_sha256": self.action_semantics.definition_sha256,
474
+ },
475
+ "finite_contract_identity_sha256": (
476
+ self.finite_variation_contract.identity_sha256
477
+ ),
478
+ "card_snapshot_sha256": self.card_snapshot_sha256,
479
+ "source_registry_sha256": (
480
+ None
481
+ if self.source_registry is None
482
+ else self.source_registry.registry_sha256
483
+ ),
484
+ "evidence_mode": self.evidence_mode.value,
485
+ "experimental_view_receipt": (
486
+ None
487
+ if self.experimental_view_receipt is None
488
+ else {
489
+ "arm": self.experimental_view_receipt.arm.value,
490
+ "receipt_sha256": self.experimental_view_receipt.receipt_sha256,
491
+ }
492
+ ),
493
+ "parent_metric_values": [
494
+ value.to_record() for value in self.parent_metric_values
495
+ ],
496
+ "metric_scales": [value.to_record() for value in self.metric_scales],
497
+ "max_output_tokens": self.max_output_tokens,
498
+ "temperature_hex": (
499
+ None if self.temperature is None else float(self.temperature).hex()
500
+ ),
501
+ }
502
+
503
+ @property
504
+ def request_sha256(self) -> str:
505
+ return _hash(_REQUEST_DOMAIN, self.to_record())
506
+
507
+
508
+ @dataclass(frozen=True, slots=True, eq=False)
509
+ class ActionForecastPartitionPolicyBinding:
510
+ """Identified, resource-bounded policy for partitioning one target frame."""
511
+
512
+ policy_id: str
513
+ policy_version: int
514
+ policy_definition_sha256: str
515
+ max_rows_per_block: int
516
+ max_metric_cells_per_block: int
517
+
518
+ def __post_init__(self) -> None:
519
+ if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
520
+ raise ValueError("policy_id must use the closed token grammar")
521
+ if type(self.policy_version) is not int or self.policy_version <= 0:
522
+ raise ValueError("policy_version must be a positive exact integer")
523
+ require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
524
+ if type(self.max_rows_per_block) is not int or self.max_rows_per_block <= 0:
525
+ raise ValueError("max_rows_per_block must be a positive exact integer")
526
+ if (
527
+ type(self.max_metric_cells_per_block) is not int
528
+ or self.max_metric_cells_per_block <= 0
529
+ ):
530
+ raise ValueError(
531
+ "max_metric_cells_per_block must be a positive exact integer"
532
+ )
533
+
534
+ def _unsigned_record(self) -> dict[str, object]:
535
+ self.__post_init__()
536
+ return {
537
+ "schema_version": 1,
538
+ "policy_id": self.policy_id,
539
+ "policy_version": self.policy_version,
540
+ "policy_definition_sha256": self.policy_definition_sha256,
541
+ "max_rows_per_block": self.max_rows_per_block,
542
+ "max_metric_cells_per_block": self.max_metric_cells_per_block,
543
+ }
544
+
545
+ @property
546
+ def binding_sha256(self) -> str:
547
+ return _hash(_PARTITION_POLICY_DOMAIN, self._unsigned_record())
548
+
549
+ def to_record(self) -> dict[str, object]:
550
+ return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
551
+
552
+ def __eq__(self, other: object) -> bool:
553
+ return (
554
+ type(self) is ActionForecastPartitionPolicyBinding
555
+ and type(other) is ActionForecastPartitionPolicyBinding
556
+ and self.binding_sha256 == other.binding_sha256
557
+ )
558
+
559
+ __hash__ = None
560
+
561
+
562
+ @dataclass(frozen=True, slots=True, eq=False)
563
+ class ActionForecastBlockSpec:
564
+ """One immutable contiguous slice of global finite-option positions."""
565
+
566
+ block_index: int
567
+ global_row_start: int
568
+ global_row_stop: int
569
+ option_identity_sha256s: tuple[str, ...]
570
+
571
+ def __post_init__(self) -> None:
572
+ if type(self.block_index) is not int or self.block_index < 0:
573
+ raise ValueError("block_index must be a non-negative exact integer")
574
+ if type(self.global_row_start) is not int or self.global_row_start < 0:
575
+ raise ValueError(
576
+ "global_row_start must be a non-negative exact integer"
577
+ )
578
+ if (
579
+ type(self.global_row_stop) is not int
580
+ or self.global_row_stop <= self.global_row_start
581
+ ):
582
+ raise ValueError("global_row_stop must be greater than global_row_start")
583
+ if type(self.option_identity_sha256s) is not tuple or any(
584
+ type(value) is not str for value in self.option_identity_sha256s
585
+ ):
586
+ raise TypeError(
587
+ "option_identity_sha256s must be an exact tuple of digests"
588
+ )
589
+ if len(self.option_identity_sha256s) != self.row_count:
590
+ raise ValueError("block option identities must cover every local row")
591
+ for index, value in enumerate(self.option_identity_sha256s):
592
+ require_sha256(value, f"option_identity_sha256s[{index}]")
593
+ if len(set(self.option_identity_sha256s)) != len(
594
+ self.option_identity_sha256s
595
+ ):
596
+ raise ValueError("a block cannot repeat an option identity")
597
+
598
+ @property
599
+ def row_count(self) -> int:
600
+ return self.global_row_stop - self.global_row_start
601
+
602
+ def _unsigned_record(self) -> dict[str, object]:
603
+ self.__post_init__()
604
+ return {
605
+ "schema_version": 1,
606
+ "block_index": self.block_index,
607
+ "global_row_start": self.global_row_start,
608
+ "global_row_stop": self.global_row_stop,
609
+ "option_identity_sha256s": list(self.option_identity_sha256s),
610
+ }
611
+
612
+ @property
613
+ def block_spec_sha256(self) -> str:
614
+ return _hash(_BLOCK_SPEC_DOMAIN, self._unsigned_record())
615
+
616
+ def to_record(self) -> dict[str, object]:
617
+ return {
618
+ **self._unsigned_record(),
619
+ "block_spec_sha256": self.block_spec_sha256,
620
+ }
621
+
622
+ def __eq__(self, other: object) -> bool:
623
+ return (
624
+ type(self) is ActionForecastBlockSpec
625
+ and type(other) is ActionForecastBlockSpec
626
+ and self.block_spec_sha256 == other.block_spec_sha256
627
+ )
628
+
629
+ __hash__ = None
630
+
631
+
632
+ @dataclass(frozen=True, slots=True, eq=False)
633
+ class ActionForecastPartitionLayout:
634
+ """Complete deterministic partition of a sealed option/metric frame."""
635
+
636
+ finite_contract_identity_sha256: str
637
+ option_identity_sha256s: tuple[str, ...]
638
+ metric_ids: tuple[str, ...]
639
+ partition_policy: ActionForecastPartitionPolicyBinding
640
+ blocks: tuple[ActionForecastBlockSpec, ...]
641
+
642
+ def __post_init__(self) -> None:
643
+ require_sha256(
644
+ self.finite_contract_identity_sha256,
645
+ "finite_contract_identity_sha256",
646
+ )
647
+ if type(self.option_identity_sha256s) is not tuple or not (
648
+ self.option_identity_sha256s
649
+ ) or any(type(value) is not str for value in self.option_identity_sha256s):
650
+ raise ValueError(
651
+ "option_identity_sha256s must be a non-empty exact tuple"
652
+ )
653
+ for index, value in enumerate(self.option_identity_sha256s):
654
+ require_sha256(value, f"option_identity_sha256s[{index}]")
655
+ if len(set(self.option_identity_sha256s)) != len(
656
+ self.option_identity_sha256s
657
+ ):
658
+ raise ValueError("partition layouts cannot repeat an option identity")
659
+ _canonical_metric_ids(self.metric_ids, "metric_ids")
660
+ if type(self.partition_policy) is not ActionForecastPartitionPolicyBinding:
661
+ raise TypeError(
662
+ "partition_policy must be an exact partition-policy binding"
663
+ )
664
+ self.partition_policy.__post_init__()
665
+ if type(self.blocks) is not tuple or not self.blocks or any(
666
+ type(value) is not ActionForecastBlockSpec for value in self.blocks
667
+ ):
668
+ raise ValueError("blocks must be a non-empty exact tuple of block specs")
669
+
670
+ cursor = 0
671
+ metric_count = len(self.metric_ids)
672
+ for expected_index, block in enumerate(self.blocks):
673
+ block.__post_init__()
674
+ if block.block_index != expected_index:
675
+ raise ValueError("block indices must be contiguous canonical positions")
676
+ if block.global_row_start != cursor:
677
+ raise ValueError("blocks must provide gap-free, overlap-free coverage")
678
+ if block.global_row_stop > self.row_count:
679
+ raise ValueError("a block extends beyond the global option frame")
680
+ expected_identities = self.option_identity_sha256s[
681
+ block.global_row_start : block.global_row_stop
682
+ ]
683
+ if block.option_identity_sha256s != expected_identities:
684
+ raise ValueError("block option identities differ from their global slice")
685
+ if block.row_count > self.partition_policy.max_rows_per_block:
686
+ raise ValueError("a block exceeds max_rows_per_block")
687
+ if (
688
+ block.row_count * metric_count
689
+ > self.partition_policy.max_metric_cells_per_block
690
+ ):
691
+ raise ValueError("a block exceeds max_metric_cells_per_block")
692
+ cursor = block.global_row_stop
693
+ if cursor != self.row_count:
694
+ raise ValueError("blocks do not completely cover the global option frame")
695
+
696
+ @property
697
+ def row_count(self) -> int:
698
+ return len(self.option_identity_sha256s)
699
+
700
+ @property
701
+ def block_count(self) -> int:
702
+ return len(self.blocks)
703
+
704
+ def _unsigned_record(self) -> dict[str, object]:
705
+ self.__post_init__()
706
+ return {
707
+ "schema_version": 1,
708
+ "finite_contract_identity_sha256": (
709
+ self.finite_contract_identity_sha256
710
+ ),
711
+ "option_identity_sha256s": list(self.option_identity_sha256s),
712
+ "metric_ids": list(self.metric_ids),
713
+ "partition_policy": self.partition_policy.to_record(),
714
+ "blocks": [value.to_record() for value in self.blocks],
715
+ }
716
+
717
+ @property
718
+ def layout_sha256(self) -> str:
719
+ return _hash(_PARTITION_LAYOUT_DOMAIN, self._unsigned_record())
720
+
721
+ def to_record(self) -> dict[str, object]:
722
+ return {**self._unsigned_record(), "layout_sha256": self.layout_sha256}
723
+
724
+ def __eq__(self, other: object) -> bool:
725
+ return (
726
+ type(self) is ActionForecastPartitionLayout
727
+ and type(other) is ActionForecastPartitionLayout
728
+ and self.layout_sha256 == other.layout_sha256
729
+ )
730
+
731
+ __hash__ = None
732
+
733
+
734
+ def validate_action_forecast_partition_layout(
735
+ request: ActionForecastRequest,
736
+ layout: ActionForecastPartitionLayout,
737
+ ) -> None:
738
+ """Bind a reusable partition geometry to one exact forecast request."""
739
+
740
+ if type(request) is not ActionForecastRequest:
741
+ raise TypeError("request must be an exact ActionForecastRequest")
742
+ request.__post_init__()
743
+ if type(layout) is not ActionForecastPartitionLayout:
744
+ raise TypeError("layout must be an exact ActionForecastPartitionLayout")
745
+ layout.__post_init__()
746
+ contract = request.finite_variation_contract
747
+ if layout.finite_contract_identity_sha256 != contract.identity_sha256:
748
+ raise ValueError("partition layout is bound to a different finite contract")
749
+ if layout.option_identity_sha256s != tuple(
750
+ option.identity_sha256 for option in contract.options
751
+ ):
752
+ raise ValueError("partition layout option frame differs from the request")
753
+ if layout.metric_ids != request.required_metric_ids:
754
+ raise ValueError("partition layout metric frame differs from the request")
755
+
756
+
757
+ @dataclass(frozen=True, slots=True, eq=False)
758
+ class ActionForecastBlockRequest:
759
+ """One exact provider-neutral block call within a global logical request."""
760
+
761
+ request: ActionForecastRequest
762
+ layout: ActionForecastPartitionLayout
763
+ block: ActionForecastBlockSpec
764
+ block_call_id: LLMCallId
765
+
766
+ def __post_init__(self) -> None:
767
+ if type(self.request) is not ActionForecastRequest:
768
+ raise TypeError("request must be an exact ActionForecastRequest")
769
+ self.request.__post_init__()
770
+ validate_action_forecast_partition_layout(self.request, self.layout)
771
+ if type(self.block) is not ActionForecastBlockSpec:
772
+ raise TypeError("block must be an exact ActionForecastBlockSpec")
773
+ self.block.__post_init__()
774
+ if self.block.block_index >= self.layout.block_count:
775
+ raise ValueError("block index is outside the partition layout")
776
+ if self.block != self.layout.blocks[self.block.block_index]:
777
+ raise ValueError("block differs from its exact partition-layout position")
778
+ if type(self.block_call_id) is not LLMCallId:
779
+ raise TypeError("block_call_id must be an exact LLMCallId")
780
+ LLMCallId.__post_init__(self.block_call_id)
781
+
782
+ def _unsigned_record(self) -> dict[str, object]:
783
+ self.__post_init__()
784
+ return {
785
+ "schema_version": 1,
786
+ "request_sha256": self.request.request_sha256,
787
+ "layout_sha256": self.layout.layout_sha256,
788
+ "block_spec_sha256": self.block.block_spec_sha256,
789
+ "block_index": self.block.block_index,
790
+ "block_call_id": self.block_call_id.value,
791
+ }
792
+
793
+ @property
794
+ def block_request_sha256(self) -> str:
795
+ return _hash(_BLOCK_REQUEST_DOMAIN, self._unsigned_record())
796
+
797
+ def to_record(self) -> dict[str, object]:
798
+ return {
799
+ **self._unsigned_record(),
800
+ "block_request_sha256": self.block_request_sha256,
801
+ }
802
+
803
+ def __eq__(self, other: object) -> bool:
804
+ return (
805
+ type(self) is ActionForecastBlockRequest
806
+ and type(other) is ActionForecastBlockRequest
807
+ and self.block_request_sha256 == other.block_request_sha256
808
+ )
809
+
810
+ __hash__ = None
811
+
812
+
813
+ @dataclass(frozen=True, slots=True)
814
+ class ResolvedActionEvidenceCitation:
815
+ """Trusted citation resolved against the exact prompt-visible card view."""
816
+
817
+ card_key: str
818
+ card_source_binding_sha256: str
819
+ action_binding_identity_sha256: str
820
+ contrast_id: str
821
+ source_option_id: str
822
+ source_option_identity_sha256: str
823
+ source_contract_identity_sha256: str
824
+
825
+ def __post_init__(self) -> None:
826
+ if type(self.card_key) is not str or _TOKEN.fullmatch(self.card_key) is None:
827
+ raise ValueError("card_key must use the closed lowercase token grammar")
828
+ if type(self.source_option_id) is not str or _OPTION_ID.fullmatch(
829
+ self.source_option_id
830
+ ) is None:
831
+ raise ValueError("source_option_id must use the finite-option grammar")
832
+ for name in (
833
+ "card_source_binding_sha256",
834
+ "action_binding_identity_sha256",
835
+ "contrast_id",
836
+ "source_option_identity_sha256",
837
+ "source_contract_identity_sha256",
838
+ ):
839
+ require_sha256(getattr(self, name), name)
840
+
841
+ def to_record(self) -> dict[str, object]:
842
+ self.__post_init__()
843
+ return {
844
+ "card_key": self.card_key,
845
+ "card_source_binding_sha256": self.card_source_binding_sha256,
846
+ "action_binding_identity_sha256": self.action_binding_identity_sha256,
847
+ "contrast_id": self.contrast_id,
848
+ "source_option_id": self.source_option_id,
849
+ "source_option_identity_sha256": self.source_option_identity_sha256,
850
+ "source_contract_identity_sha256": self.source_contract_identity_sha256,
851
+ }
852
+
853
+
854
+ @dataclass(frozen=True, slots=True)
855
+ class ResolvedActionMetricForecast:
856
+ metric_id: str
857
+ p10_delta: float
858
+ p50_delta: float
859
+ p90_delta: float
860
+ confidence: float
861
+ citations: tuple[ResolvedActionEvidenceCitation, ...]
862
+
863
+ def __post_init__(self) -> None:
864
+ if type(self.citations) is not tuple or any(
865
+ type(value) is not ResolvedActionEvidenceCitation
866
+ for value in self.citations
867
+ ):
868
+ raise TypeError("citations must be an exact tuple of resolved citations")
869
+ for value in self.citations:
870
+ value.__post_init__()
871
+ unresolved = ActionMetricForecast(
872
+ metric_id=self.metric_id,
873
+ p10_delta=self.p10_delta,
874
+ p50_delta=self.p50_delta,
875
+ p90_delta=self.p90_delta,
876
+ confidence=self.confidence,
877
+ citations=tuple(
878
+ ActionEvidenceCitation(
879
+ card_key=value.card_key,
880
+ action_binding_identity_sha256=(
881
+ value.action_binding_identity_sha256
882
+ ),
883
+ )
884
+ for value in self.citations
885
+ ),
886
+ )
887
+ del unresolved
888
+
889
+ def to_record(self) -> dict[str, object]:
890
+ self.__post_init__()
891
+ return {
892
+ "metric_id": self.metric_id,
893
+ "p10_delta_hex": self.p10_delta.hex(),
894
+ "p50_delta_hex": self.p50_delta.hex(),
895
+ "p90_delta_hex": self.p90_delta.hex(),
896
+ "confidence_hex": self.confidence.hex(),
897
+ "citations": [value.to_record() for value in self.citations],
898
+ }
899
+
900
+
901
+ @dataclass(frozen=True, slots=True)
902
+ class ResolvedActionForecast:
903
+ option_id: str
904
+ option_identity_sha256: str
905
+ child_configuration_sha256: str
906
+ family: str
907
+ probability_valid: float
908
+ metric_forecasts: tuple[ResolvedActionMetricForecast, ...]
909
+
910
+ def __post_init__(self) -> None:
911
+ if type(self.option_id) is not str or _OPTION_ID.fullmatch(self.option_id) is None:
912
+ raise ValueError("option_id must use the finite-option grammar")
913
+ if type(self.family) is not str or _TOKEN.fullmatch(self.family) is None:
914
+ raise ValueError("family must use the closed token grammar")
915
+ require_sha256(self.option_identity_sha256, "option_identity_sha256")
916
+ require_sha256(
917
+ self.child_configuration_sha256,
918
+ "child_configuration_sha256",
919
+ )
920
+ _finite_float(self.probability_valid, "probability_valid")
921
+ if not 0.0 <= self.probability_valid <= 1.0:
922
+ raise ValueError("probability_valid must lie in [0,1]")
923
+ if type(self.metric_forecasts) is not tuple or not self.metric_forecasts or any(
924
+ type(value) is not ResolvedActionMetricForecast
925
+ for value in self.metric_forecasts
926
+ ):
927
+ raise ValueError("metric_forecasts must contain resolved values")
928
+ for value in self.metric_forecasts:
929
+ value.__post_init__()
930
+ _canonical_metric_ids(
931
+ tuple(value.metric_id for value in self.metric_forecasts),
932
+ "metric_forecasts",
933
+ )
934
+
935
+ def to_record(self) -> dict[str, object]:
936
+ self.__post_init__()
937
+ return {
938
+ "option_id": self.option_id,
939
+ "option_identity_sha256": self.option_identity_sha256,
940
+ "child_configuration_sha256": self.child_configuration_sha256,
941
+ "family": self.family,
942
+ "probability_valid_hex": self.probability_valid.hex(),
943
+ "metric_forecasts": [
944
+ value.to_record() for value in self.metric_forecasts
945
+ ],
946
+ }
947
+
948
+
949
+ @dataclass(frozen=True, slots=True, eq=False)
950
+ class ResolvedActionForecastBlock:
951
+ """Trusted partial forecast receipt for one exact partition block."""
952
+
953
+ request_sha256: str
954
+ block_request_sha256: str
955
+ layout_sha256: str
956
+ block_spec_sha256: str
957
+ block_index: int
958
+ forecasts: tuple[ResolvedActionForecast, ...]
959
+ policy_id: str
960
+ policy_version: int
961
+ policy_definition_sha256: str
962
+
963
+ def __post_init__(self) -> None:
964
+ for name in (
965
+ "request_sha256",
966
+ "block_request_sha256",
967
+ "layout_sha256",
968
+ "block_spec_sha256",
969
+ "policy_definition_sha256",
970
+ ):
971
+ require_sha256(getattr(self, name), name)
972
+ if type(self.block_index) is not int or self.block_index < 0:
973
+ raise ValueError("block_index must be a non-negative exact integer")
974
+ if type(self.forecasts) is not tuple or not self.forecasts or any(
975
+ type(value) is not ResolvedActionForecast for value in self.forecasts
976
+ ):
977
+ raise ValueError("forecasts must be a non-empty exact resolved tuple")
978
+ for value in self.forecasts:
979
+ value.__post_init__()
980
+ option_ids = tuple(value.option_id for value in self.forecasts)
981
+ if len(set(option_ids)) != len(option_ids):
982
+ raise ValueError("a resolved block cannot repeat an option")
983
+ if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
984
+ raise ValueError("policy_id must use the closed token grammar")
985
+ if type(self.policy_version) is not int or self.policy_version <= 0:
986
+ raise ValueError("policy_version must be a positive exact integer")
987
+
988
+ def _unsigned_record(self) -> dict[str, object]:
989
+ self.__post_init__()
990
+ return {
991
+ "schema_version": 1,
992
+ "request_sha256": self.request_sha256,
993
+ "block_request_sha256": self.block_request_sha256,
994
+ "layout_sha256": self.layout_sha256,
995
+ "block_spec_sha256": self.block_spec_sha256,
996
+ "block_index": self.block_index,
997
+ "forecasts": [value.to_record() for value in self.forecasts],
998
+ "policy_id": self.policy_id,
999
+ "policy_version": self.policy_version,
1000
+ "policy_definition_sha256": self.policy_definition_sha256,
1001
+ }
1002
+
1003
+ @property
1004
+ def receipt_sha256(self) -> str:
1005
+ return _hash(_RESOLVED_BLOCK_DOMAIN, self._unsigned_record())
1006
+
1007
+ def to_record(self) -> dict[str, object]:
1008
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
1009
+
1010
+ def __eq__(self, other: object) -> bool:
1011
+ return (
1012
+ type(self) is ResolvedActionForecastBlock
1013
+ and type(other) is ResolvedActionForecastBlock
1014
+ and self.receipt_sha256 == other.receipt_sha256
1015
+ )
1016
+
1017
+ __hash__ = None
1018
+
1019
+
1020
+ @dataclass(frozen=True, slots=True, eq=False)
1021
+ class ResolvedActionForecastBatch:
1022
+ """Complete all-option forecast receipt bound to one exact request."""
1023
+
1024
+ request_sha256: str
1025
+ context_sha256: str
1026
+ optimization_semantics_definition_sha256: str
1027
+ action_semantics_definition_sha256: str
1028
+ finite_contract_identity_sha256: str
1029
+ card_snapshot_sha256: str
1030
+ forecasts: tuple[ResolvedActionForecast, ...]
1031
+ policy_id: str
1032
+ policy_version: int
1033
+ policy_definition_sha256: str
1034
+
1035
+ def __post_init__(self) -> None:
1036
+ for name in (
1037
+ "request_sha256",
1038
+ "context_sha256",
1039
+ "optimization_semantics_definition_sha256",
1040
+ "action_semantics_definition_sha256",
1041
+ "finite_contract_identity_sha256",
1042
+ "card_snapshot_sha256",
1043
+ "policy_definition_sha256",
1044
+ ):
1045
+ require_sha256(getattr(self, name), name)
1046
+ if type(self.forecasts) is not tuple or not self.forecasts or any(
1047
+ type(value) is not ResolvedActionForecast for value in self.forecasts
1048
+ ):
1049
+ raise ValueError("forecasts must be a non-empty exact resolved tuple")
1050
+ for value in self.forecasts:
1051
+ value.__post_init__()
1052
+ option_ids = tuple(value.option_id for value in self.forecasts)
1053
+ if len(set(option_ids)) != len(option_ids):
1054
+ raise ValueError("resolved forecasts cannot repeat an option")
1055
+ if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
1056
+ raise ValueError("policy_id must use the closed token grammar")
1057
+ if type(self.policy_version) is not int or self.policy_version <= 0:
1058
+ raise ValueError("policy_version must be a positive exact integer")
1059
+
1060
+ def _unsigned_record(self) -> dict[str, object]:
1061
+ self.__post_init__()
1062
+ return {
1063
+ "schema_version": 2,
1064
+ "request_sha256": self.request_sha256,
1065
+ "context_sha256": self.context_sha256,
1066
+ "optimization_semantics_definition_sha256": (
1067
+ self.optimization_semantics_definition_sha256
1068
+ ),
1069
+ "action_semantics_definition_sha256": (
1070
+ self.action_semantics_definition_sha256
1071
+ ),
1072
+ "finite_contract_identity_sha256": self.finite_contract_identity_sha256,
1073
+ "card_snapshot_sha256": self.card_snapshot_sha256,
1074
+ "forecasts": [value.to_record() for value in self.forecasts],
1075
+ "policy_id": self.policy_id,
1076
+ "policy_version": self.policy_version,
1077
+ "policy_definition_sha256": self.policy_definition_sha256,
1078
+ }
1079
+
1080
+ @property
1081
+ def receipt_sha256(self) -> str:
1082
+ return _hash(_BATCH_DOMAIN, self._unsigned_record())
1083
+
1084
+ def to_record(self) -> dict[str, object]:
1085
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
1086
+
1087
+ def __eq__(self, other: object) -> bool:
1088
+ return (
1089
+ type(self) is ResolvedActionForecastBatch
1090
+ and type(other) is ResolvedActionForecastBatch
1091
+ and self.receipt_sha256 == other.receipt_sha256
1092
+ )
1093
+
1094
+ __hash__ = None
1095
+
1096
+
1097
+ def _resolved_citation_index(
1098
+ request: ActionForecastRequest,
1099
+ ) -> dict[tuple[str, str], ResolvedActionEvidenceCitation]:
1100
+ """Resolve each immutable prompt-visible citation exactly once.
1101
+
1102
+ Callers validate the complete request before constructing this index. The
1103
+ request and every nested card/binding are frozen values, so rebuilding and
1104
+ revalidating the same provenance graph for every option/metric citation
1105
+ adds no protection. Keeping the exact card key in the lookup key preserves
1106
+ the original rejection of cross-card and ambiguous binding references.
1107
+ """
1108
+
1109
+ index: dict[tuple[str, str], ResolvedActionEvidenceCitation] = {}
1110
+ for card in request.cards:
1111
+ if card.source_binding is None:
1112
+ raise ValueError("forecast request contains an unbound evidence card")
1113
+ for binding in _prompt_action_bindings(card):
1114
+ key = (card.card_key, binding.identity_sha256)
1115
+ if key in index:
1116
+ raise ValueError(
1117
+ "forecast request contains an ambiguous prompt action binding"
1118
+ )
1119
+ index[key] = ResolvedActionEvidenceCitation(
1120
+ card_key=card.card_key,
1121
+ card_source_binding_sha256=card.source_binding.binding_sha256,
1122
+ action_binding_identity_sha256=binding.identity_sha256,
1123
+ contrast_id=binding.contrast_id,
1124
+ source_option_id=binding.option_id,
1125
+ source_option_identity_sha256=binding.option_identity_sha256,
1126
+ source_contract_identity_sha256=binding.contract_identity_sha256,
1127
+ )
1128
+ return index
1129
+
1130
+
1131
+ def _resolve_indexed_citation(
1132
+ citation_index: dict[tuple[str, str], ResolvedActionEvidenceCitation],
1133
+ *,
1134
+ prompt_card_keys: frozenset[str],
1135
+ citation: ActionEvidenceCitation,
1136
+ ) -> ResolvedActionEvidenceCitation:
1137
+ if citation.card_key not in prompt_card_keys:
1138
+ raise ValueError("forecast cites a card outside the request snapshot")
1139
+ resolved = citation_index.get(
1140
+ (citation.card_key, citation.action_binding_identity_sha256)
1141
+ )
1142
+ if resolved is None:
1143
+ raise ValueError("forecast cites an absent or ambiguous prompt action binding")
1144
+ return resolved
1145
+
1146
+
1147
+ def resolve_action_forecast_block(
1148
+ block_request: ActionForecastBlockRequest,
1149
+ drafts: tuple[ActionForecastDraft, ...],
1150
+ *,
1151
+ policy_id: str,
1152
+ policy_version: int,
1153
+ policy_definition_sha256: str,
1154
+ ) -> ResolvedActionForecastBlock:
1155
+ """Resolve exactly one block without representing it as a complete batch."""
1156
+
1157
+ if type(block_request) is not ActionForecastBlockRequest:
1158
+ raise TypeError("block_request must be an exact ActionForecastBlockRequest")
1159
+ block_request.__post_init__()
1160
+ request = block_request.request
1161
+ block_spec = block_request.block
1162
+ options = request.finite_variation_contract.options[
1163
+ block_spec.global_row_start : block_spec.global_row_stop
1164
+ ]
1165
+ if type(drafts) is not tuple or any(
1166
+ type(value) is not ActionForecastDraft for value in drafts
1167
+ ):
1168
+ raise TypeError("drafts must be an exact tuple of ActionForecastDraft")
1169
+ for value in drafts:
1170
+ value.__post_init__()
1171
+ if tuple(value.option_id for value in drafts) != tuple(
1172
+ option.option_id for option in options
1173
+ ):
1174
+ raise ValueError(
1175
+ "block forecast order/coverage differs from its global option slice"
1176
+ )
1177
+
1178
+ required_metric_ids = request.required_metric_ids
1179
+ citation_index = _resolved_citation_index(request)
1180
+ prompt_card_keys = frozenset(card.card_key for card in request.cards)
1181
+ resolved: list[ResolvedActionForecast] = []
1182
+ for option, draft in zip(options, drafts, strict=True):
1183
+ if tuple(value.metric_id for value in draft.metric_forecasts) != (
1184
+ required_metric_ids
1185
+ ):
1186
+ raise ValueError("every block row must forecast every required metric")
1187
+ for metric in draft.metric_forecasts:
1188
+ if request.evidence_mode is ActionForecastEvidenceMode.GROUNDED:
1189
+ if not metric.citations:
1190
+ raise ValueError(
1191
+ "grounded block forecasts require citations for every metric"
1192
+ )
1193
+ elif metric.citations:
1194
+ raise ValueError("catalog-only block forecasts forbid citations")
1195
+ resolved.append(
1196
+ ResolvedActionForecast(
1197
+ option_id=option.option_id,
1198
+ option_identity_sha256=option.identity_sha256,
1199
+ child_configuration_sha256=option.child_configuration_sha256,
1200
+ family=option.family,
1201
+ probability_valid=draft.probability_valid,
1202
+ metric_forecasts=tuple(
1203
+ ResolvedActionMetricForecast(
1204
+ metric_id=metric.metric_id,
1205
+ p10_delta=metric.p10_delta,
1206
+ p50_delta=metric.p50_delta,
1207
+ p90_delta=metric.p90_delta,
1208
+ confidence=metric.confidence,
1209
+ citations=tuple(
1210
+ _resolve_indexed_citation(
1211
+ citation_index,
1212
+ prompt_card_keys=prompt_card_keys,
1213
+ citation=citation,
1214
+ )
1215
+ for citation in metric.citations
1216
+ ),
1217
+ )
1218
+ for metric in draft.metric_forecasts
1219
+ ),
1220
+ )
1221
+ )
1222
+ result = ResolvedActionForecastBlock(
1223
+ request_sha256=request.request_sha256,
1224
+ block_request_sha256=block_request.block_request_sha256,
1225
+ layout_sha256=block_request.layout.layout_sha256,
1226
+ block_spec_sha256=block_spec.block_spec_sha256,
1227
+ block_index=block_spec.block_index,
1228
+ forecasts=tuple(resolved),
1229
+ policy_id=policy_id,
1230
+ policy_version=policy_version,
1231
+ policy_definition_sha256=policy_definition_sha256,
1232
+ )
1233
+ validate_resolved_action_forecast_block(block_request, result)
1234
+ return result
1235
+
1236
+
1237
+ def validate_resolved_action_forecast_block(
1238
+ block_request: ActionForecastBlockRequest,
1239
+ block: ResolvedActionForecastBlock,
1240
+ ) -> None:
1241
+ """Revalidate one resolved partial receipt against its global positions."""
1242
+
1243
+ if type(block_request) is not ActionForecastBlockRequest:
1244
+ raise TypeError("block_request must be an exact ActionForecastBlockRequest")
1245
+ block_request.__post_init__()
1246
+ if type(block) is not ResolvedActionForecastBlock:
1247
+ raise TypeError("block must be an exact ResolvedActionForecastBlock")
1248
+ block.__post_init__()
1249
+ request = block_request.request
1250
+ spec = block_request.block
1251
+ if (
1252
+ block.request_sha256 != request.request_sha256
1253
+ or block.block_request_sha256 != block_request.block_request_sha256
1254
+ or block.layout_sha256 != block_request.layout.layout_sha256
1255
+ or block.block_spec_sha256 != spec.block_spec_sha256
1256
+ or block.block_index != spec.block_index
1257
+ ):
1258
+ raise ValueError("resolved block is bound to a different block request")
1259
+ options = request.finite_variation_contract.options[
1260
+ spec.global_row_start : spec.global_row_stop
1261
+ ]
1262
+ if tuple(value.option_id for value in block.forecasts) != tuple(
1263
+ option.option_id for option in options
1264
+ ):
1265
+ raise ValueError("resolved block order/coverage differs from its global slice")
1266
+
1267
+ required_metric_ids = request.required_metric_ids
1268
+ citation_index = _resolved_citation_index(request)
1269
+ prompt_card_keys = frozenset(card.card_key for card in request.cards)
1270
+ for option, forecast in zip(options, block.forecasts, strict=True):
1271
+ if (
1272
+ forecast.option_identity_sha256 != option.identity_sha256
1273
+ or forecast.child_configuration_sha256
1274
+ != option.child_configuration_sha256
1275
+ or forecast.family != option.family
1276
+ ):
1277
+ raise ValueError("resolved block differs from its sealed finite option")
1278
+ if tuple(value.metric_id for value in forecast.metric_forecasts) != (
1279
+ required_metric_ids
1280
+ ):
1281
+ raise ValueError("resolved block metric coverage differs from request")
1282
+ for metric in forecast.metric_forecasts:
1283
+ if request.evidence_mode is ActionForecastEvidenceMode.GROUNDED:
1284
+ if not metric.citations:
1285
+ raise ValueError(
1286
+ "grounded resolved blocks require citations for every metric"
1287
+ )
1288
+ elif metric.citations:
1289
+ raise ValueError("catalog-only resolved blocks forbid citations")
1290
+ for citation in metric.citations:
1291
+ unresolved = ActionEvidenceCitation(
1292
+ card_key=citation.card_key,
1293
+ action_binding_identity_sha256=(
1294
+ citation.action_binding_identity_sha256
1295
+ ),
1296
+ )
1297
+ expected = _resolve_indexed_citation(
1298
+ citation_index,
1299
+ prompt_card_keys=prompt_card_keys,
1300
+ citation=unresolved,
1301
+ )
1302
+ if citation != expected:
1303
+ raise ValueError(
1304
+ "resolved block citation differs from prompt card evidence"
1305
+ )
1306
+
1307
+
1308
+ def resolve_action_forecasts(
1309
+ request: ActionForecastRequest,
1310
+ drafts: tuple[ActionForecastDraft, ...],
1311
+ *,
1312
+ policy_id: str,
1313
+ policy_version: int,
1314
+ policy_definition_sha256: str,
1315
+ ) -> ResolvedActionForecastBatch:
1316
+ """Resolve one complete all-option output or publish no forecast receipt."""
1317
+
1318
+ if type(request) is not ActionForecastRequest:
1319
+ raise TypeError("request must be an exact ActionForecastRequest")
1320
+ request.__post_init__()
1321
+ required_metric_ids = tuple(
1322
+ value.metric_id for value in request.parent_metric_values
1323
+ )
1324
+ request_sha256 = request.request_sha256
1325
+ context_sha256 = typed_json_sha256(request.context)
1326
+ contract = request.finite_variation_contract
1327
+ contract_identity_sha256 = contract.identity_sha256
1328
+ card_snapshot_sha256 = portfolio_card_snapshot_sha256(request.cards)
1329
+ if type(drafts) is not tuple or any(
1330
+ type(value) is not ActionForecastDraft for value in drafts
1331
+ ):
1332
+ raise TypeError("drafts must be an exact tuple of ActionForecastDraft")
1333
+ for value in drafts:
1334
+ value.__post_init__()
1335
+ option_ids = tuple(value.option_id for value in drafts)
1336
+ if len(set(option_ids)) != len(option_ids):
1337
+ raise ValueError("forecast output repeats a finite option")
1338
+ expected = {option.option_id for option in contract.options}
1339
+ foreign = set(option_ids) - expected
1340
+ if foreign:
1341
+ raise ValueError("forecast output contains a foreign finite option")
1342
+ missing = expected - set(option_ids)
1343
+ if missing:
1344
+ raise ValueError("forecast output is incomplete for the finite contract")
1345
+ by_option = {value.option_id: value for value in drafts}
1346
+ citation_index = _resolved_citation_index(request)
1347
+ prompt_card_keys = frozenset(card.card_key for card in request.cards)
1348
+ resolved: list[ResolvedActionForecast] = []
1349
+ for option in contract.options:
1350
+ draft = by_option[option.option_id]
1351
+ if tuple(value.metric_id for value in draft.metric_forecasts) != (
1352
+ required_metric_ids
1353
+ ):
1354
+ raise ValueError("every option must forecast every required metric exactly")
1355
+ for metric in draft.metric_forecasts:
1356
+ if request.evidence_mode is ActionForecastEvidenceMode.GROUNDED:
1357
+ if not metric.citations:
1358
+ raise ValueError(
1359
+ "grounded forecasts require card/action citations for every metric"
1360
+ )
1361
+ elif metric.citations:
1362
+ raise ValueError("catalog-only forecasts forbid evidence citations")
1363
+ resolved.append(
1364
+ ResolvedActionForecast(
1365
+ option_id=option.option_id,
1366
+ option_identity_sha256=option.identity_sha256,
1367
+ child_configuration_sha256=option.child_configuration_sha256,
1368
+ family=option.family,
1369
+ probability_valid=draft.probability_valid,
1370
+ metric_forecasts=tuple(
1371
+ ResolvedActionMetricForecast(
1372
+ metric_id=metric.metric_id,
1373
+ p10_delta=metric.p10_delta,
1374
+ p50_delta=metric.p50_delta,
1375
+ p90_delta=metric.p90_delta,
1376
+ confidence=metric.confidence,
1377
+ citations=tuple(
1378
+ _resolve_indexed_citation(
1379
+ citation_index,
1380
+ prompt_card_keys=prompt_card_keys,
1381
+ citation=citation,
1382
+ )
1383
+ for citation in metric.citations
1384
+ ),
1385
+ )
1386
+ for metric in draft.metric_forecasts
1387
+ ),
1388
+ )
1389
+ )
1390
+ batch = ResolvedActionForecastBatch(
1391
+ request_sha256=request_sha256,
1392
+ context_sha256=context_sha256,
1393
+ optimization_semantics_definition_sha256=(
1394
+ request.optimization_semantics.definition_sha256
1395
+ ),
1396
+ action_semantics_definition_sha256=(
1397
+ request.action_semantics.definition_sha256
1398
+ ),
1399
+ finite_contract_identity_sha256=contract_identity_sha256,
1400
+ card_snapshot_sha256=card_snapshot_sha256,
1401
+ forecasts=tuple(resolved),
1402
+ policy_id=policy_id,
1403
+ policy_version=policy_version,
1404
+ policy_definition_sha256=policy_definition_sha256,
1405
+ )
1406
+ validate_resolved_action_forecasts(request, batch)
1407
+ return batch
1408
+
1409
+
1410
+ def validate_resolved_action_forecasts(
1411
+ request: ActionForecastRequest,
1412
+ batch: ResolvedActionForecastBatch,
1413
+ ) -> None:
1414
+ """Revalidate a forecast receipt against its exact trusted request."""
1415
+
1416
+ if type(request) is not ActionForecastRequest:
1417
+ raise TypeError("request must be an exact ActionForecastRequest")
1418
+ request.__post_init__()
1419
+ required_metric_ids = tuple(
1420
+ value.metric_id for value in request.parent_metric_values
1421
+ )
1422
+ request_sha256 = request.request_sha256
1423
+ context_sha256 = typed_json_sha256(request.context)
1424
+ contract = request.finite_variation_contract
1425
+ contract_identity_sha256 = contract.identity_sha256
1426
+ card_snapshot_sha256 = portfolio_card_snapshot_sha256(request.cards)
1427
+ options_by_id = {option.option_id: option for option in contract.options}
1428
+ if type(batch) is not ResolvedActionForecastBatch:
1429
+ raise TypeError("batch must be an exact ResolvedActionForecastBatch")
1430
+ batch.__post_init__()
1431
+ if (
1432
+ batch.request_sha256 != request_sha256
1433
+ or batch.context_sha256 != context_sha256
1434
+ or batch.optimization_semantics_definition_sha256
1435
+ != request.optimization_semantics.definition_sha256
1436
+ or batch.action_semantics_definition_sha256
1437
+ != request.action_semantics.definition_sha256
1438
+ or batch.finite_contract_identity_sha256
1439
+ != contract_identity_sha256
1440
+ or batch.card_snapshot_sha256 != card_snapshot_sha256
1441
+ ):
1442
+ raise ValueError("forecast batch is bound to a different request snapshot")
1443
+ if tuple(value.option_id for value in batch.forecasts) != tuple(
1444
+ option.option_id for option in contract.options
1445
+ ):
1446
+ raise ValueError("resolved forecast order/coverage differs from the contract")
1447
+ citation_index = _resolved_citation_index(request)
1448
+ prompt_card_keys = frozenset(card.card_key for card in request.cards)
1449
+ for forecast in batch.forecasts:
1450
+ option = options_by_id.get(forecast.option_id)
1451
+ if option is None:
1452
+ raise ValueError("resolved forecast option is outside the sealed contract")
1453
+ if (
1454
+ forecast.option_identity_sha256 != option.identity_sha256
1455
+ or forecast.child_configuration_sha256
1456
+ != option.child_configuration_sha256
1457
+ or forecast.family != option.family
1458
+ ):
1459
+ raise ValueError("resolved forecast differs from its sealed finite option")
1460
+ if tuple(value.metric_id for value in forecast.metric_forecasts) != (
1461
+ required_metric_ids
1462
+ ):
1463
+ raise ValueError("resolved forecast metric coverage differs from request")
1464
+ for metric in forecast.metric_forecasts:
1465
+ if request.evidence_mode is ActionForecastEvidenceMode.GROUNDED:
1466
+ if not metric.citations:
1467
+ raise ValueError(
1468
+ "grounded resolved forecasts require card/action "
1469
+ "citations for every metric"
1470
+ )
1471
+ elif metric.citations:
1472
+ raise ValueError(
1473
+ "catalog-only resolved forecasts forbid evidence citations"
1474
+ )
1475
+ for citation in metric.citations:
1476
+ unresolved = ActionEvidenceCitation(
1477
+ card_key=citation.card_key,
1478
+ action_binding_identity_sha256=(
1479
+ citation.action_binding_identity_sha256
1480
+ ),
1481
+ )
1482
+ expected = _resolve_indexed_citation(
1483
+ citation_index,
1484
+ prompt_card_keys=prompt_card_keys,
1485
+ citation=unresolved,
1486
+ )
1487
+ if citation != expected:
1488
+ raise ValueError("resolved citation differs from prompt card evidence")
1489
+
1490
+
1491
+ @dataclass(frozen=True, slots=True)
1492
+ class ActionForecastResult:
1493
+ forecasts: ResolvedActionForecastBatch
1494
+ telemetry: AgenticCallTelemetry | None
1495
+
1496
+ def __post_init__(self) -> None:
1497
+ if type(self.forecasts) is not ResolvedActionForecastBatch:
1498
+ raise TypeError("forecasts must be an exact ResolvedActionForecastBatch")
1499
+ self.forecasts.__post_init__()
1500
+ if self.telemetry is not None:
1501
+ if type(self.telemetry) is not AgenticCallTelemetry:
1502
+ raise TypeError("telemetry must be exact AgenticCallTelemetry or None")
1503
+ self.telemetry.__post_init__()
1504
+
1505
+
1506
+ @dataclass(frozen=True, slots=True)
1507
+ class ActionForecastBlockResult:
1508
+ """One physical block execution and its optional provider telemetry."""
1509
+
1510
+ forecasts: ResolvedActionForecastBlock
1511
+ telemetry: AgenticCallTelemetry | None
1512
+
1513
+ def __post_init__(self) -> None:
1514
+ if type(self.forecasts) is not ResolvedActionForecastBlock:
1515
+ raise TypeError(
1516
+ "forecasts must be an exact ResolvedActionForecastBlock"
1517
+ )
1518
+ self.forecasts.__post_init__()
1519
+ if self.telemetry is not None:
1520
+ if type(self.telemetry) is not AgenticCallTelemetry:
1521
+ raise TypeError("telemetry must be exact AgenticCallTelemetry or None")
1522
+ self.telemetry.__post_init__()
1523
+
1524
+
1525
+ @runtime_checkable
1526
+ class ActionForecastPolicy(Protocol):
1527
+ """Forecast every sealed option; partial provider results cannot escape."""
1528
+
1529
+ async def forecast(self, request: ActionForecastRequest) -> ActionForecastResult: ...
1530
+
1531
+
1532
+ @runtime_checkable
1533
+ class ActionForecastBlockPolicy(Protocol):
1534
+ """Forecast one sealed block; the application owns global reassembly."""
1535
+
1536
+ async def forecast_block(
1537
+ self,
1538
+ request: ActionForecastBlockRequest,
1539
+ ) -> ActionForecastBlockResult: ...
1540
+
1541
+
1542
+ __all__ = [
1543
+ "ActionEvidenceCitation",
1544
+ "ActionForecastBlockPolicy",
1545
+ "ActionForecastBlockRequest",
1546
+ "ActionForecastBlockResult",
1547
+ "ActionForecastBlockSpec",
1548
+ "ActionForecastDraft",
1549
+ "ActionForecastEvidenceMode",
1550
+ "ActionForecastPartitionLayout",
1551
+ "ActionForecastPartitionPolicyBinding",
1552
+ "ActionForecastPolicy",
1553
+ "ActionForecastRequest",
1554
+ "ActionForecastResult",
1555
+ "ActionMetricForecast",
1556
+ "MetricForecastScale",
1557
+ "ParentMetricValue",
1558
+ "ResolvedActionEvidenceCitation",
1559
+ "ResolvedActionForecast",
1560
+ "ResolvedActionForecastBatch",
1561
+ "ResolvedActionForecastBlock",
1562
+ "ResolvedActionMetricForecast",
1563
+ "resolve_action_forecast_block",
1564
+ "resolve_action_forecasts",
1565
+ "validate_action_forecast_partition_layout",
1566
+ "validate_resolved_action_forecast_block",
1567
+ "validate_resolved_action_forecasts",
1568
+ ]