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,1154 @@
1
+ """Prequential calibration for current-prefix archive opportunity.
2
+
3
+ The raw language-model forecast remains useful semantic evidence, but it is
4
+ not assumed to be a calibrated numerical distribution. This module exposes a
5
+ small application port that converts a raw conditional archive-opportunity
6
+ forecast into a prior-only predictive distribution and an abstention decision.
7
+
8
+ All features are workload opaque: evolutionary stage, model/expert lane,
9
+ typed operator, native rank, prior score, forecast reliability, and observed
10
+ prefix scale. Objective names, workload identifiers, simulator fields,
11
+ provider names, and current eligible-candidate outcomes are absent.
12
+
13
+ The default empirical implementation uses:
14
+
15
+ * archive-prefix normalization for cross-workload scale transport;
16
+ * log residuals for multiplicative forecast error;
17
+ * a hierarchy of stage/lane/operator calibration cells;
18
+ * a prequential Spearman skill gate;
19
+ * support-distance abstention;
20
+ * a finite-sample residual lower quantile; and
21
+ * a two-part lower expected-opportunity bound that separates probability of a
22
+ positive contribution from its positive magnitude.
23
+
24
+ This is recommendation evidence only. The protected incumbent remains the
25
+ selection authority whenever the calibrator abstains.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import hashlib
31
+ import json
32
+ import math
33
+ import re
34
+ from dataclasses import dataclass, field
35
+ from enum import Enum
36
+ from statistics import fmean
37
+ from typing import Protocol, runtime_checkable
38
+
39
+ from agent_evolve.domain.patch import require_sha256
40
+
41
+
42
+ PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID = (
43
+ "hierarchical_prequential_archive_opportunity"
44
+ )
45
+ PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION = 1
46
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
47
+ _OBSERVATION_DOMAIN = (
48
+ b"agent-evolve:archive-opportunity-calibration-observation:v1\x00"
49
+ )
50
+ _CONTEXT_DOMAIN = (
51
+ b"agent-evolve:archive-opportunity-calibration-context:v1\x00"
52
+ )
53
+ _RESULT_DOMAIN = (
54
+ b"agent-evolve:archive-opportunity-calibration-result:v1\x00"
55
+ )
56
+ _SNAPSHOT_DOMAIN = (
57
+ b"agent-evolve:archive-opportunity-calibration-snapshot:v1\x00"
58
+ )
59
+ _DEFINITION_DOMAIN = (
60
+ b"agent-evolve:archive-opportunity-calibration-policy:v1\x00"
61
+ )
62
+
63
+
64
+ def _canonical_json(value: object) -> bytes:
65
+ return json.dumps(
66
+ value,
67
+ allow_nan=False,
68
+ ensure_ascii=True,
69
+ separators=(",", ":"),
70
+ sort_keys=True,
71
+ ).encode("ascii", errors="strict")
72
+
73
+
74
+ def _hash(domain: bytes, value: object) -> str:
75
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
76
+
77
+
78
+ def _require_token(value: str, *, name: str) -> None:
79
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
80
+ raise ValueError(f"{name} must use the closed token grammar")
81
+
82
+
83
+ def _require_probability(value: float, *, name: str) -> None:
84
+ if (
85
+ type(value) is not float
86
+ or not math.isfinite(value)
87
+ or not 0.0 <= value <= 1.0
88
+ ):
89
+ raise ValueError(f"{name} must lie in [0, 1]")
90
+
91
+
92
+ def _require_nonnegative(value: float, *, name: str) -> None:
93
+ if (
94
+ type(value) is not float
95
+ or not math.isfinite(value)
96
+ or value < 0.0
97
+ ):
98
+ raise ValueError(f"{name} must be finite and non-negative")
99
+
100
+
101
+ def _nearest_rank_quantile(
102
+ values: tuple[float, ...],
103
+ probability: float,
104
+ ) -> float:
105
+ """Return the deterministic finite-sample nearest-rank quantile."""
106
+
107
+ if not values:
108
+ raise ValueError("a quantile requires at least one observation")
109
+ _require_probability(probability, name="probability")
110
+ if any(type(value) is not float or not math.isfinite(value) for value in values):
111
+ raise ValueError("quantile values must be finite exact floats")
112
+ ordered = tuple(sorted(values))
113
+ rank = max(1, math.ceil(probability * len(ordered)))
114
+ return ordered[min(len(ordered), rank) - 1]
115
+
116
+
117
+ def _rank(values: tuple[float, ...]) -> tuple[float, ...]:
118
+ order = sorted(range(len(values)), key=lambda index: values[index])
119
+ result = [0.0] * len(values)
120
+ cursor = 0
121
+ while cursor < len(order):
122
+ end = cursor + 1
123
+ while (
124
+ end < len(order)
125
+ and values[order[end]] == values[order[cursor]]
126
+ ):
127
+ end += 1
128
+ average = (cursor + 1 + end) / 2.0
129
+ for position in range(cursor, end):
130
+ result[order[position]] = average
131
+ cursor = end
132
+ return tuple(result)
133
+
134
+
135
+ def _pearson(
136
+ left: tuple[float, ...],
137
+ right: tuple[float, ...],
138
+ ) -> float | None:
139
+ if len(left) != len(right) or len(left) < 2:
140
+ return None
141
+ left_mean = fmean(left)
142
+ right_mean = fmean(right)
143
+ numerator = math.fsum(
144
+ (x - left_mean) * (y - right_mean)
145
+ for x, y in zip(left, right, strict=True)
146
+ )
147
+ left_norm = math.sqrt(
148
+ math.fsum((value - left_mean) ** 2 for value in left)
149
+ )
150
+ right_norm = math.sqrt(
151
+ math.fsum((value - right_mean) ** 2 for value in right)
152
+ )
153
+ if left_norm == 0.0 or right_norm == 0.0:
154
+ return None
155
+ return float(numerator / (left_norm * right_norm))
156
+
157
+
158
+ def _spearman(
159
+ left: tuple[float, ...],
160
+ right: tuple[float, ...],
161
+ ) -> float | None:
162
+ return _pearson(_rank(left), _rank(right))
163
+
164
+
165
+ def _wilson_lower_bound(
166
+ *,
167
+ positive_count: int,
168
+ observation_count: int,
169
+ z_value: float,
170
+ ) -> float:
171
+ if (
172
+ type(positive_count) is not int
173
+ or type(observation_count) is not int
174
+ or not 0 <= positive_count <= observation_count
175
+ or observation_count <= 0
176
+ ):
177
+ raise ValueError("Wilson counts are invalid")
178
+ if type(z_value) is not float or not math.isfinite(z_value) or z_value <= 0.0:
179
+ raise ValueError("z_value must be a positive finite float")
180
+ probability = positive_count / observation_count
181
+ z_squared = z_value * z_value
182
+ denominator = 1.0 + z_squared / observation_count
183
+ centre = probability + z_squared / (2.0 * observation_count)
184
+ radius = z_value * math.sqrt(
185
+ (
186
+ probability * (1.0 - probability)
187
+ + z_squared / (4.0 * observation_count)
188
+ )
189
+ / observation_count
190
+ )
191
+ return float(max(0.0, (centre - radius) / denominator))
192
+
193
+
194
+ @dataclass(frozen=True, slots=True)
195
+ class ArchiveOpportunityActionContext:
196
+ """Outcome-blind generic context for one candidate opportunity."""
197
+
198
+ action_sha256: str
199
+ decision_index: int
200
+ lane_id: str
201
+ operator_id: str
202
+ native_rank: int
203
+ lane_size: int
204
+ prior_score: float
205
+ parent_generated_in_current_run: bool
206
+ context_sha256: str = field(init=False)
207
+
208
+ def __post_init__(self) -> None:
209
+ require_sha256(self.action_sha256, "action_sha256")
210
+ if type(self.decision_index) is not int or self.decision_index <= 0:
211
+ raise ValueError("decision_index must be positive")
212
+ _require_token(self.lane_id, name="lane_id")
213
+ _require_token(self.operator_id, name="operator_id")
214
+ if (
215
+ type(self.native_rank) is not int
216
+ or type(self.lane_size) is not int
217
+ or self.native_rank <= 0
218
+ or self.lane_size <= 0
219
+ or self.native_rank > self.lane_size
220
+ ):
221
+ raise ValueError("native rank must fit the positive lane size")
222
+ _require_probability(self.prior_score, name="prior_score")
223
+ if type(self.parent_generated_in_current_run) is not bool:
224
+ raise TypeError(
225
+ "parent_generated_in_current_run must be exact"
226
+ )
227
+ object.__setattr__(
228
+ self,
229
+ "context_sha256",
230
+ _hash(_CONTEXT_DOMAIN, self._unsigned_record()),
231
+ )
232
+
233
+ @property
234
+ def rank_quality(self) -> float:
235
+ if self.lane_size == 1:
236
+ return 1.0
237
+ return 1.0 - (
238
+ (self.native_rank - 1) / float(self.lane_size - 1)
239
+ )
240
+
241
+ def _unsigned_record(self) -> dict[str, object]:
242
+ return {
243
+ "schema_version": 1,
244
+ "action_sha256": self.action_sha256,
245
+ "decision_index": self.decision_index,
246
+ "lane_id": self.lane_id,
247
+ "operator_id": self.operator_id,
248
+ "native_rank": self.native_rank,
249
+ "lane_size": self.lane_size,
250
+ "rank_quality_hex": self.rank_quality.hex(),
251
+ "prior_score_hex": self.prior_score.hex(),
252
+ "parent_generated_in_current_run": (
253
+ self.parent_generated_in_current_run
254
+ ),
255
+ "workload_objective_provider_prompt_fields_present": False,
256
+ }
257
+
258
+ def to_record(self) -> dict[str, object]:
259
+ self.__post_init__()
260
+ return {
261
+ **self._unsigned_record(),
262
+ "context_sha256": self.context_sha256,
263
+ }
264
+
265
+
266
+ @dataclass(frozen=True, slots=True)
267
+ class ArchiveOpportunityCalibrationRequest:
268
+ """One raw current-prefix forecast submitted for calibration."""
269
+
270
+ context: ArchiveOpportunityActionContext
271
+ forecast_reliability: float
272
+ raw_adverse_gain: float
273
+ raw_central_gain: float
274
+ raw_favorable_gain: float
275
+ raw_acquisition_value: float
276
+ prefix_gain: float
277
+ prefix_action_count: int
278
+
279
+ def __post_init__(self) -> None:
280
+ if type(self.context) is not ArchiveOpportunityActionContext:
281
+ raise TypeError("context must be exact")
282
+ self.context.__post_init__()
283
+ _require_probability(
284
+ self.forecast_reliability,
285
+ name="forecast_reliability",
286
+ )
287
+ for name in (
288
+ "raw_adverse_gain",
289
+ "raw_central_gain",
290
+ "raw_favorable_gain",
291
+ "raw_acquisition_value",
292
+ "prefix_gain",
293
+ ):
294
+ _require_nonnegative(getattr(self, name), name=name)
295
+ if (
296
+ type(self.prefix_action_count) is not int
297
+ or self.prefix_action_count <= 0
298
+ ):
299
+ raise ValueError("prefix_action_count must be positive")
300
+ if self.raw_adverse_gain > self.raw_favorable_gain:
301
+ raise ValueError("adverse gain cannot exceed favorable gain")
302
+
303
+ @property
304
+ def prefix_mean_gain(self) -> float:
305
+ return self.prefix_gain / self.prefix_action_count
306
+
307
+ def to_record(self) -> dict[str, object]:
308
+ self.__post_init__()
309
+ return {
310
+ "schema_version": 1,
311
+ "context": self.context.to_record(),
312
+ "forecast_reliability_hex": self.forecast_reliability.hex(),
313
+ "raw_adverse_gain_hex": self.raw_adverse_gain.hex(),
314
+ "raw_central_gain_hex": self.raw_central_gain.hex(),
315
+ "raw_favorable_gain_hex": self.raw_favorable_gain.hex(),
316
+ "raw_acquisition_value_hex": (
317
+ self.raw_acquisition_value.hex()
318
+ ),
319
+ "prefix_gain_hex": self.prefix_gain.hex(),
320
+ "prefix_action_count": self.prefix_action_count,
321
+ "prefix_mean_gain_hex": self.prefix_mean_gain.hex(),
322
+ "eligible_candidate_outcomes_observed": False,
323
+ }
324
+
325
+
326
+ class ArchiveOpportunityCalibrationEvidenceRole(str, Enum):
327
+ """How the forecast target entered the authenticated evidence stream."""
328
+
329
+ AUTHORITATIVE_SELECTED = "authoritative_selected"
330
+ SAME_PREFIX_PAIRED_AUTHORITATIVE = (
331
+ "same_prefix_paired_authoritative"
332
+ )
333
+ SAME_PREFIX_PAIRED_COUNTERFACTUAL = (
334
+ "same_prefix_paired_counterfactual"
335
+ )
336
+
337
+
338
+ @dataclass(frozen=True, slots=True)
339
+ class ArchiveOpportunityCalibrationObservation:
340
+ """One forecast joined to a later exact conditional-gain observation."""
341
+
342
+ request: ArchiveOpportunityCalibrationRequest
343
+ realized_conditional_gain: float
344
+ decision_sha256: str
345
+ outcome_sha256: str
346
+ evidence_cutoff_ordinal: int
347
+ evidence_role: ArchiveOpportunityCalibrationEvidenceRole = (
348
+ ArchiveOpportunityCalibrationEvidenceRole.AUTHORITATIVE_SELECTED
349
+ )
350
+ sampling_propensity: float = 1.0
351
+ paired_observation_sha256: str | None = None
352
+ observation_sha256: str = field(init=False)
353
+
354
+ def __post_init__(self) -> None:
355
+ if type(self.request) is not ArchiveOpportunityCalibrationRequest:
356
+ raise TypeError("request must be exact")
357
+ self.request.__post_init__()
358
+ _require_nonnegative(
359
+ self.realized_conditional_gain,
360
+ name="realized_conditional_gain",
361
+ )
362
+ require_sha256(self.decision_sha256, "decision_sha256")
363
+ require_sha256(self.outcome_sha256, "outcome_sha256")
364
+ if (
365
+ type(self.evidence_cutoff_ordinal) is not int
366
+ or self.evidence_cutoff_ordinal <= 0
367
+ ):
368
+ raise ValueError("evidence_cutoff_ordinal must be positive")
369
+ if (
370
+ type(self.evidence_role)
371
+ is not ArchiveOpportunityCalibrationEvidenceRole
372
+ ):
373
+ raise TypeError("evidence_role must be exact")
374
+ if (
375
+ type(self.sampling_propensity) is not float
376
+ or not math.isfinite(self.sampling_propensity)
377
+ or not 0.0 < self.sampling_propensity <= 1.0
378
+ ):
379
+ raise ValueError(
380
+ "sampling_propensity must be a finite positive probability"
381
+ )
382
+ if self.paired_observation_sha256 is not None:
383
+ require_sha256(
384
+ self.paired_observation_sha256,
385
+ "paired_observation_sha256",
386
+ )
387
+ is_legacy_schema = (
388
+ self.evidence_role
389
+ is (
390
+ ArchiveOpportunityCalibrationEvidenceRole
391
+ .AUTHORITATIVE_SELECTED
392
+ )
393
+ and self.sampling_propensity == 1.0
394
+ and self.paired_observation_sha256 is None
395
+ )
396
+ if (
397
+ not is_legacy_schema
398
+ and self.paired_observation_sha256 is None
399
+ ):
400
+ raise ValueError(
401
+ "paired calibration evidence requires its observation identity"
402
+ )
403
+ object.__setattr__(
404
+ self,
405
+ "observation_sha256",
406
+ _hash(_OBSERVATION_DOMAIN, self._unsigned_record()),
407
+ )
408
+
409
+ @property
410
+ def positive(self) -> bool:
411
+ return self.realized_conditional_gain > 0.0
412
+
413
+ def _unsigned_record(self) -> dict[str, object]:
414
+ result: dict[str, object] = {
415
+ "schema_version": 1,
416
+ "request": self.request.to_record(),
417
+ "realized_conditional_gain_hex": (
418
+ self.realized_conditional_gain.hex()
419
+ ),
420
+ "positive": self.positive,
421
+ "decision_sha256": self.decision_sha256,
422
+ "outcome_sha256": self.outcome_sha256,
423
+ "evidence_cutoff_ordinal": self.evidence_cutoff_ordinal,
424
+ "current_or_future_candidate_outcomes_used": False,
425
+ }
426
+ if (
427
+ self.evidence_role
428
+ is not (
429
+ ArchiveOpportunityCalibrationEvidenceRole
430
+ .AUTHORITATIVE_SELECTED
431
+ )
432
+ or self.sampling_propensity != 1.0
433
+ or self.paired_observation_sha256 is not None
434
+ ):
435
+ result.update(
436
+ {
437
+ "schema_version": 2,
438
+ "evidence_role": self.evidence_role.value,
439
+ "sampling_propensity_hex": (
440
+ self.sampling_propensity.hex()
441
+ ),
442
+ "paired_observation_sha256": (
443
+ self.paired_observation_sha256
444
+ ),
445
+ "same_prefix_counterfactual_is_not_archive_"
446
+ "publication": (
447
+ self.evidence_role
448
+ is (
449
+ ArchiveOpportunityCalibrationEvidenceRole
450
+ .SAME_PREFIX_PAIRED_COUNTERFACTUAL
451
+ )
452
+ ),
453
+ }
454
+ )
455
+ return result
456
+
457
+ def to_record(self) -> dict[str, object]:
458
+ self.__post_init__()
459
+ return {
460
+ **self._unsigned_record(),
461
+ "observation_sha256": self.observation_sha256,
462
+ }
463
+
464
+
465
+ @dataclass(frozen=True, slots=True)
466
+ class ArchiveOpportunityCalibrationResult:
467
+ """Authenticated calibrated opportunity and abstention evidence."""
468
+
469
+ action_sha256: str
470
+ calibration_id: str
471
+ calibration_version: int
472
+ calibration_definition_sha256: str
473
+ observation_snapshot_sha256: str
474
+ selected_stratum: str
475
+ stratum_support_count: int
476
+ skill_support_count: int
477
+ prequential_rank_skill: float | None
478
+ raw_normalized_acquisition: float
479
+ minimum_supported_normalized_acquisition: float | None
480
+ maximum_supported_normalized_acquisition: float | None
481
+ support_log_distance: float
482
+ positive_count: int
483
+ posterior_positive_probability: float
484
+ lower_positive_probability: float
485
+ conformal_lower_gain: float
486
+ lower_expected_gain: float
487
+ calibrated_acquisition_value: float
488
+ calibrated_upper_gain: float
489
+ abstained: bool
490
+ abstention_reason: str | None
491
+ result_sha256: str = field(init=False)
492
+
493
+ def __post_init__(self) -> None:
494
+ require_sha256(self.action_sha256, "action_sha256")
495
+ _require_token(self.calibration_id, name="calibration_id")
496
+ if (
497
+ type(self.calibration_version) is not int
498
+ or self.calibration_version <= 0
499
+ ):
500
+ raise ValueError("calibration_version must be positive")
501
+ require_sha256(
502
+ self.calibration_definition_sha256,
503
+ "calibration_definition_sha256",
504
+ )
505
+ require_sha256(
506
+ self.observation_snapshot_sha256,
507
+ "observation_snapshot_sha256",
508
+ )
509
+ _require_token(self.selected_stratum, name="selected_stratum")
510
+ for name in (
511
+ "stratum_support_count",
512
+ "skill_support_count",
513
+ "positive_count",
514
+ ):
515
+ value = getattr(self, name)
516
+ if type(value) is not int or value < 0:
517
+ raise ValueError(f"{name} must be non-negative")
518
+ if self.positive_count > self.stratum_support_count:
519
+ raise ValueError("positive_count exceeds stratum support")
520
+ if self.prequential_rank_skill is not None and (
521
+ type(self.prequential_rank_skill) is not float
522
+ or not math.isfinite(self.prequential_rank_skill)
523
+ or not -1.0 <= self.prequential_rank_skill <= 1.0
524
+ ):
525
+ raise ValueError("prequential_rank_skill must lie in [-1,1]")
526
+ for name in (
527
+ "raw_normalized_acquisition",
528
+ "support_log_distance",
529
+ "conformal_lower_gain",
530
+ "lower_expected_gain",
531
+ "calibrated_acquisition_value",
532
+ "calibrated_upper_gain",
533
+ ):
534
+ _require_nonnegative(getattr(self, name), name=name)
535
+ for name in (
536
+ "minimum_supported_normalized_acquisition",
537
+ "maximum_supported_normalized_acquisition",
538
+ ):
539
+ value = getattr(self, name)
540
+ if value is not None:
541
+ _require_nonnegative(value, name=name)
542
+ if (
543
+ self.minimum_supported_normalized_acquisition is None
544
+ ) != (
545
+ self.maximum_supported_normalized_acquisition is None
546
+ ):
547
+ raise ValueError("support bounds must be jointly present")
548
+ _require_probability(
549
+ self.posterior_positive_probability,
550
+ name="posterior_positive_probability",
551
+ )
552
+ _require_probability(
553
+ self.lower_positive_probability,
554
+ name="lower_positive_probability",
555
+ )
556
+ if (
557
+ self.lower_positive_probability
558
+ > self.posterior_positive_probability
559
+ ):
560
+ raise ValueError("lower probability exceeds posterior mean")
561
+ if self.abstained:
562
+ if self.abstention_reason is None:
563
+ raise ValueError("abstention requires a reason")
564
+ _require_token(
565
+ self.abstention_reason,
566
+ name="abstention_reason",
567
+ )
568
+ elif self.abstention_reason is not None:
569
+ raise ValueError(
570
+ "a calibrated recommendation cannot have an abstention reason"
571
+ )
572
+ object.__setattr__(
573
+ self,
574
+ "result_sha256",
575
+ _hash(_RESULT_DOMAIN, self._unsigned_record()),
576
+ )
577
+
578
+ def _unsigned_record(self) -> dict[str, object]:
579
+ return {
580
+ "schema_version": 1,
581
+ "action_sha256": self.action_sha256,
582
+ "calibration": {
583
+ "calibration_id": self.calibration_id,
584
+ "calibration_version": self.calibration_version,
585
+ "definition_sha256": (
586
+ self.calibration_definition_sha256
587
+ ),
588
+ },
589
+ "observation_snapshot_sha256": (
590
+ self.observation_snapshot_sha256
591
+ ),
592
+ "selected_stratum": self.selected_stratum,
593
+ "stratum_support_count": self.stratum_support_count,
594
+ "skill_support_count": self.skill_support_count,
595
+ "prequential_rank_skill_hex": (
596
+ None
597
+ if self.prequential_rank_skill is None
598
+ else self.prequential_rank_skill.hex()
599
+ ),
600
+ "raw_normalized_acquisition_hex": (
601
+ self.raw_normalized_acquisition.hex()
602
+ ),
603
+ "minimum_supported_normalized_acquisition_hex": (
604
+ None
605
+ if self.minimum_supported_normalized_acquisition is None
606
+ else self.minimum_supported_normalized_acquisition.hex()
607
+ ),
608
+ "maximum_supported_normalized_acquisition_hex": (
609
+ None
610
+ if self.maximum_supported_normalized_acquisition is None
611
+ else self.maximum_supported_normalized_acquisition.hex()
612
+ ),
613
+ "support_log_distance_hex": self.support_log_distance.hex(),
614
+ "positive_count": self.positive_count,
615
+ "posterior_positive_probability_hex": (
616
+ self.posterior_positive_probability.hex()
617
+ ),
618
+ "lower_positive_probability_hex": (
619
+ self.lower_positive_probability.hex()
620
+ ),
621
+ "conformal_lower_gain_hex": self.conformal_lower_gain.hex(),
622
+ "lower_expected_gain_hex": self.lower_expected_gain.hex(),
623
+ "calibrated_acquisition_value_hex": (
624
+ self.calibrated_acquisition_value.hex()
625
+ ),
626
+ "calibrated_upper_gain_hex": (
627
+ self.calibrated_upper_gain.hex()
628
+ ),
629
+ "abstained": self.abstained,
630
+ "abstention_reason": self.abstention_reason,
631
+ "eligible_candidate_outcomes_observed": False,
632
+ }
633
+
634
+ def to_record(self) -> dict[str, object]:
635
+ self.__post_init__()
636
+ return {
637
+ **self._unsigned_record(),
638
+ "result_sha256": self.result_sha256,
639
+ }
640
+
641
+
642
+ @runtime_checkable
643
+ class ArchiveOpportunityCalibrationPort(Protocol):
644
+ """Stable inverted API for prior-only opportunity calibration."""
645
+
646
+ calibration_id: str
647
+ calibration_version: int
648
+ definition_sha256: str
649
+
650
+ def calibrate(
651
+ self,
652
+ request: ArchiveOpportunityCalibrationRequest,
653
+ ) -> ArchiveOpportunityCalibrationResult: ...
654
+
655
+
656
+ def validate_archive_opportunity_calibration_port(
657
+ value: ArchiveOpportunityCalibrationPort,
658
+ ) -> tuple[str, int, str]:
659
+ if not isinstance(value, ArchiveOpportunityCalibrationPort):
660
+ raise TypeError(
661
+ "calibration must implement ArchiveOpportunityCalibrationPort"
662
+ )
663
+ identity = (
664
+ value.calibration_id,
665
+ value.calibration_version,
666
+ value.definition_sha256,
667
+ )
668
+ _require_token(identity[0], name="calibration_id")
669
+ if type(identity[1]) is not int or identity[1] <= 0:
670
+ raise ValueError("calibration_version must be positive")
671
+ require_sha256(identity[2], "calibration definition_sha256")
672
+ return identity
673
+
674
+
675
+ @dataclass(frozen=True, slots=True)
676
+ class HierarchicalPrequentialArchiveOpportunityCalibration:
677
+ """Calibrate raw opportunity using a sealed prior observation snapshot."""
678
+
679
+ observations: tuple[ArchiveOpportunityCalibrationObservation, ...]
680
+ maximum_evidence_cutoff_ordinal: int
681
+ minimum_global_support: int = 6
682
+ minimum_cell_support: int = 4
683
+ minimum_rank_support: int = 4
684
+ minimum_rank_skill: float = 0.0
685
+ residual_lower_probability: float = 0.1
686
+ residual_upper_probability: float = 0.9
687
+ positive_prior_alpha: float = 1.0
688
+ positive_prior_beta: float = 1.0
689
+ wilson_z_value: float = 1.0
690
+ maximum_support_log_distance: float = 1.0
691
+ minimum_lower_expected_gain: float = 0.0
692
+ scale_floor: float = 1e-15
693
+ calibration_id: str = (
694
+ PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID
695
+ )
696
+ calibration_version: int = (
697
+ PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION
698
+ )
699
+ observation_snapshot_sha256: str = field(init=False)
700
+ definition_sha256: str = field(init=False)
701
+
702
+ def __post_init__(self) -> None:
703
+ if (
704
+ type(self.observations) is not tuple
705
+ or any(
706
+ type(value)
707
+ is not ArchiveOpportunityCalibrationObservation
708
+ for value in self.observations
709
+ )
710
+ ):
711
+ raise TypeError("observations must contain exact observations")
712
+ for value in self.observations:
713
+ value.__post_init__()
714
+ hashes = tuple(value.observation_sha256 for value in self.observations)
715
+ if hashes != tuple(sorted(set(hashes))):
716
+ raise ValueError(
717
+ "observations must be unique and hash canonical"
718
+ )
719
+ if (
720
+ type(self.maximum_evidence_cutoff_ordinal) is not int
721
+ or self.maximum_evidence_cutoff_ordinal <= 0
722
+ ):
723
+ raise ValueError(
724
+ "maximum_evidence_cutoff_ordinal must be positive"
725
+ )
726
+ if any(
727
+ value.evidence_cutoff_ordinal
728
+ > self.maximum_evidence_cutoff_ordinal
729
+ for value in self.observations
730
+ ):
731
+ raise ValueError(
732
+ "observation crosses the sealed evidence cutoff"
733
+ )
734
+ for name in (
735
+ "minimum_global_support",
736
+ "minimum_cell_support",
737
+ "minimum_rank_support",
738
+ ):
739
+ value = getattr(self, name)
740
+ if type(value) is not int or value <= 0:
741
+ raise ValueError(f"{name} must be positive")
742
+ if (
743
+ type(self.minimum_rank_skill) is not float
744
+ or not math.isfinite(self.minimum_rank_skill)
745
+ or not -1.0 <= self.minimum_rank_skill <= 1.0
746
+ ):
747
+ raise ValueError("minimum_rank_skill must lie in [-1,1]")
748
+ for name in (
749
+ "residual_lower_probability",
750
+ "residual_upper_probability",
751
+ ):
752
+ _require_probability(getattr(self, name), name=name)
753
+ if (
754
+ self.residual_lower_probability
755
+ >= self.residual_upper_probability
756
+ ):
757
+ raise ValueError("residual quantile order is invalid")
758
+ for name in (
759
+ "positive_prior_alpha",
760
+ "positive_prior_beta",
761
+ "wilson_z_value",
762
+ "maximum_support_log_distance",
763
+ "scale_floor",
764
+ ):
765
+ value = getattr(self, name)
766
+ if type(value) is not float or not math.isfinite(value) or value <= 0.0:
767
+ raise ValueError(f"{name} must be positive and finite")
768
+ _require_nonnegative(
769
+ self.minimum_lower_expected_gain,
770
+ name="minimum_lower_expected_gain",
771
+ )
772
+ _require_token(self.calibration_id, name="calibration_id")
773
+ if (
774
+ self.calibration_id
775
+ != PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID
776
+ or self.calibration_version
777
+ != PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION
778
+ ):
779
+ raise ValueError("calibration identity is immutable")
780
+ snapshot = _hash(
781
+ _SNAPSHOT_DOMAIN,
782
+ {
783
+ "schema_version": 1,
784
+ "maximum_evidence_cutoff_ordinal": (
785
+ self.maximum_evidence_cutoff_ordinal
786
+ ),
787
+ "observation_sha256s": list(hashes),
788
+ "forbidden_feature_fields": [
789
+ "workload_id",
790
+ "objective_id",
791
+ "provider_id",
792
+ "prompt_id",
793
+ ],
794
+ },
795
+ )
796
+ object.__setattr__(
797
+ self,
798
+ "observation_snapshot_sha256",
799
+ snapshot,
800
+ )
801
+ object.__setattr__(
802
+ self,
803
+ "definition_sha256",
804
+ _hash(
805
+ _DEFINITION_DOMAIN,
806
+ {
807
+ "schema_version": 1,
808
+ "calibration_id": self.calibration_id,
809
+ "calibration_version": self.calibration_version,
810
+ "observation_snapshot_sha256": snapshot,
811
+ "minimum_global_support": self.minimum_global_support,
812
+ "minimum_cell_support": self.minimum_cell_support,
813
+ "minimum_rank_support": self.minimum_rank_support,
814
+ "minimum_rank_skill_hex": (
815
+ self.minimum_rank_skill.hex()
816
+ ),
817
+ "residual_lower_probability_hex": (
818
+ self.residual_lower_probability.hex()
819
+ ),
820
+ "residual_upper_probability_hex": (
821
+ self.residual_upper_probability.hex()
822
+ ),
823
+ "positive_prior_alpha_hex": (
824
+ self.positive_prior_alpha.hex()
825
+ ),
826
+ "positive_prior_beta_hex": (
827
+ self.positive_prior_beta.hex()
828
+ ),
829
+ "wilson_z_value_hex": self.wilson_z_value.hex(),
830
+ "maximum_support_log_distance_hex": (
831
+ self.maximum_support_log_distance.hex()
832
+ ),
833
+ "minimum_lower_expected_gain_hex": (
834
+ self.minimum_lower_expected_gain.hex()
835
+ ),
836
+ "scale_floor_hex": self.scale_floor.hex(),
837
+ "normalization": (
838
+ "raw_and_realized_gain_over_observed_prefix_mean_gain"
839
+ ),
840
+ "residual": "log1p_realized_minus_log1p_raw",
841
+ "strata": [
842
+ "stage_lane_operator",
843
+ "stage_operator",
844
+ "stage",
845
+ "global",
846
+ ],
847
+ "skill": "prequential_spearman_raw_vs_realized",
848
+ "lower_bound": (
849
+ "wilson_positive_probability_times_positive_"
850
+ "residual_lower_magnitude"
851
+ ),
852
+ "eligible_candidate_outcomes_observed": False,
853
+ "workload_objective_provider_prompt_branches": False,
854
+ },
855
+ ),
856
+ )
857
+
858
+ def _scale(
859
+ self,
860
+ request: ArchiveOpportunityCalibrationRequest,
861
+ ) -> float:
862
+ return max(self.scale_floor, request.prefix_mean_gain)
863
+
864
+ def _rows(
865
+ self,
866
+ request: ArchiveOpportunityCalibrationRequest,
867
+ ) -> tuple[
868
+ str,
869
+ tuple[ArchiveOpportunityCalibrationObservation, ...],
870
+ ]:
871
+ context = request.context
872
+ candidates = (
873
+ (
874
+ "stage_lane_operator",
875
+ tuple(
876
+ value
877
+ for value in self.observations
878
+ if (
879
+ value.request.context.decision_index
880
+ == context.decision_index
881
+ and value.request.context.lane_id
882
+ == context.lane_id
883
+ and value.request.context.operator_id
884
+ == context.operator_id
885
+ )
886
+ ),
887
+ ),
888
+ (
889
+ "stage_operator",
890
+ tuple(
891
+ value
892
+ for value in self.observations
893
+ if (
894
+ value.request.context.decision_index
895
+ == context.decision_index
896
+ and value.request.context.operator_id
897
+ == context.operator_id
898
+ )
899
+ ),
900
+ ),
901
+ (
902
+ "stage",
903
+ tuple(
904
+ value
905
+ for value in self.observations
906
+ if value.request.context.decision_index
907
+ == context.decision_index
908
+ ),
909
+ ),
910
+ )
911
+ for name, rows in candidates:
912
+ if len(rows) >= self.minimum_cell_support:
913
+ return name, rows
914
+ return "global", self.observations
915
+
916
+ def _normalized(
917
+ self,
918
+ observation: ArchiveOpportunityCalibrationObservation,
919
+ ) -> tuple[float, float]:
920
+ scale = self._scale(observation.request)
921
+ return (
922
+ observation.request.raw_acquisition_value / scale,
923
+ observation.realized_conditional_gain / scale,
924
+ )
925
+
926
+ def calibrate(
927
+ self,
928
+ request: ArchiveOpportunityCalibrationRequest,
929
+ ) -> ArchiveOpportunityCalibrationResult:
930
+ self.__post_init__()
931
+ if type(request) is not ArchiveOpportunityCalibrationRequest:
932
+ raise TypeError("request must be exact")
933
+ request.__post_init__()
934
+ stratum, rows = self._rows(request)
935
+ stage_rows = tuple(
936
+ value
937
+ for value in self.observations
938
+ if (
939
+ value.request.context.decision_index
940
+ == request.context.decision_index
941
+ )
942
+ )
943
+ skill_rows = (
944
+ stage_rows
945
+ if len(stage_rows) >= self.minimum_rank_support
946
+ else self.observations
947
+ )
948
+ rank_skill = (
949
+ _spearman(
950
+ tuple(
951
+ self._normalized(value)[0]
952
+ for value in skill_rows
953
+ ),
954
+ tuple(
955
+ self._normalized(value)[1]
956
+ for value in skill_rows
957
+ ),
958
+ )
959
+ if len(skill_rows) >= self.minimum_rank_support
960
+ else None
961
+ )
962
+ scale = self._scale(request)
963
+ raw_normalized = request.raw_acquisition_value / scale
964
+
965
+ if len(self.observations) < self.minimum_global_support:
966
+ abstention_reason = "insufficient_global_support"
967
+ elif len(rows) < self.minimum_cell_support:
968
+ abstention_reason = "insufficient_cell_support"
969
+ elif (
970
+ rank_skill is None
971
+ or len(skill_rows) < self.minimum_rank_support
972
+ ):
973
+ abstention_reason = "insufficient_rank_skill_support"
974
+ elif rank_skill <= self.minimum_rank_skill:
975
+ abstention_reason = "nonpositive_prequential_rank_skill"
976
+ else:
977
+ abstention_reason = None
978
+
979
+ normalized_rows = tuple(self._normalized(value) for value in rows)
980
+ supported_raw = tuple(value[0] for value in normalized_rows)
981
+ minimum_supported = min(supported_raw) if supported_raw else None
982
+ maximum_supported = max(supported_raw) if supported_raw else None
983
+ support_log_distance = 0.0
984
+ if minimum_supported is not None and maximum_supported is not None:
985
+ log_raw = math.log1p(raw_normalized)
986
+ log_minimum = math.log1p(minimum_supported)
987
+ log_maximum = math.log1p(maximum_supported)
988
+ support_log_distance = max(
989
+ 0.0,
990
+ log_minimum - log_raw,
991
+ log_raw - log_maximum,
992
+ )
993
+ if (
994
+ abstention_reason is None
995
+ and support_log_distance
996
+ > self.maximum_support_log_distance
997
+ ):
998
+ abstention_reason = "forecast_scale_out_of_support"
999
+
1000
+ residuals = tuple(
1001
+ math.log1p(realized) - math.log1p(raw)
1002
+ for raw, realized in normalized_rows
1003
+ )
1004
+ positive_rows = tuple(
1005
+ (raw, realized)
1006
+ for raw, realized in normalized_rows
1007
+ if realized > 0.0
1008
+ )
1009
+ positive_count = len(positive_rows)
1010
+ posterior_positive = (
1011
+ self.positive_prior_alpha + positive_count
1012
+ ) / (
1013
+ self.positive_prior_alpha
1014
+ + self.positive_prior_beta
1015
+ + len(rows)
1016
+ )
1017
+ lower_positive = (
1018
+ _wilson_lower_bound(
1019
+ positive_count=positive_count,
1020
+ observation_count=len(rows),
1021
+ z_value=self.wilson_z_value,
1022
+ )
1023
+ if rows
1024
+ else 0.0
1025
+ )
1026
+
1027
+ if residuals:
1028
+ lower_residual = _nearest_rank_quantile(
1029
+ residuals,
1030
+ self.residual_lower_probability,
1031
+ )
1032
+ conformal_lower_normalized = max(
1033
+ 0.0,
1034
+ math.expm1(
1035
+ math.log1p(raw_normalized) + lower_residual
1036
+ ),
1037
+ )
1038
+ else:
1039
+ conformal_lower_normalized = 0.0
1040
+
1041
+ if positive_rows:
1042
+ positive_residuals = tuple(
1043
+ math.log1p(realized) - math.log1p(raw)
1044
+ for raw, realized in positive_rows
1045
+ )
1046
+ lower_positive_residual = _nearest_rank_quantile(
1047
+ positive_residuals,
1048
+ self.residual_lower_probability,
1049
+ )
1050
+ median_positive_residual = _nearest_rank_quantile(
1051
+ positive_residuals,
1052
+ 0.5,
1053
+ )
1054
+ upper_positive_residual = _nearest_rank_quantile(
1055
+ positive_residuals,
1056
+ self.residual_upper_probability,
1057
+ )
1058
+ positive_lower_normalized = max(
1059
+ 0.0,
1060
+ math.expm1(
1061
+ math.log1p(raw_normalized)
1062
+ + lower_positive_residual
1063
+ ),
1064
+ )
1065
+ positive_median_normalized = max(
1066
+ 0.0,
1067
+ math.expm1(
1068
+ math.log1p(raw_normalized)
1069
+ + median_positive_residual
1070
+ ),
1071
+ )
1072
+ positive_upper_normalized = max(
1073
+ 0.0,
1074
+ math.expm1(
1075
+ math.log1p(raw_normalized)
1076
+ + upper_positive_residual
1077
+ ),
1078
+ )
1079
+ else:
1080
+ positive_lower_normalized = 0.0
1081
+ positive_median_normalized = 0.0
1082
+ positive_upper_normalized = 0.0
1083
+ if abstention_reason is None:
1084
+ abstention_reason = "no_positive_calibration_support"
1085
+
1086
+ conformal_lower_gain = scale * conformal_lower_normalized
1087
+ lower_expected_gain = (
1088
+ scale * lower_positive * positive_lower_normalized
1089
+ )
1090
+ calibrated_acquisition = (
1091
+ scale
1092
+ * posterior_positive
1093
+ * positive_median_normalized
1094
+ )
1095
+ calibrated_upper = scale * positive_upper_normalized
1096
+ if (
1097
+ abstention_reason is None
1098
+ and lower_expected_gain
1099
+ <= self.minimum_lower_expected_gain
1100
+ ):
1101
+ abstention_reason = "no_calibrated_lower_opportunity"
1102
+
1103
+ return ArchiveOpportunityCalibrationResult(
1104
+ action_sha256=request.context.action_sha256,
1105
+ calibration_id=self.calibration_id,
1106
+ calibration_version=self.calibration_version,
1107
+ calibration_definition_sha256=self.definition_sha256,
1108
+ observation_snapshot_sha256=(
1109
+ self.observation_snapshot_sha256
1110
+ ),
1111
+ selected_stratum=stratum,
1112
+ stratum_support_count=len(rows),
1113
+ skill_support_count=len(skill_rows),
1114
+ prequential_rank_skill=rank_skill,
1115
+ raw_normalized_acquisition=float(raw_normalized),
1116
+ minimum_supported_normalized_acquisition=(
1117
+ None
1118
+ if minimum_supported is None
1119
+ else float(minimum_supported)
1120
+ ),
1121
+ maximum_supported_normalized_acquisition=(
1122
+ None
1123
+ if maximum_supported is None
1124
+ else float(maximum_supported)
1125
+ ),
1126
+ support_log_distance=float(support_log_distance),
1127
+ positive_count=positive_count,
1128
+ posterior_positive_probability=float(
1129
+ posterior_positive
1130
+ ),
1131
+ lower_positive_probability=float(lower_positive),
1132
+ conformal_lower_gain=float(conformal_lower_gain),
1133
+ lower_expected_gain=float(lower_expected_gain),
1134
+ calibrated_acquisition_value=float(
1135
+ calibrated_acquisition
1136
+ ),
1137
+ calibrated_upper_gain=float(calibrated_upper),
1138
+ abstained=abstention_reason is not None,
1139
+ abstention_reason=abstention_reason,
1140
+ )
1141
+
1142
+
1143
+ __all__ = [
1144
+ "ArchiveOpportunityActionContext",
1145
+ "ArchiveOpportunityCalibrationEvidenceRole",
1146
+ "ArchiveOpportunityCalibrationObservation",
1147
+ "ArchiveOpportunityCalibrationPort",
1148
+ "ArchiveOpportunityCalibrationRequest",
1149
+ "ArchiveOpportunityCalibrationResult",
1150
+ "HierarchicalPrequentialArchiveOpportunityCalibration",
1151
+ "PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID",
1152
+ "PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION",
1153
+ "validate_archive_opportunity_calibration_port",
1154
+ ]