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,910 @@
1
+ """Offline reconstruction and exact replay of recorded provider exchanges.
2
+
3
+ This module has no provider, HTTP, model, or environment dependency. It reads
4
+ only validated events and sanitized canonical artifacts.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ from dataclasses import InitVar, dataclass, field
11
+ from decimal import Decimal
12
+ from enum import Enum
13
+ from types import MappingProxyType
14
+ from typing import Dict, Iterable, Mapping, Optional, Tuple
15
+
16
+ from agent_evolve.application.artifact_replay import (
17
+ ArtifactRegistrationKey,
18
+ verify_artifact_journal,
19
+ )
20
+ from agent_evolve.domain.artifact import ArtifactRef, ArtifactRole, artifact_ref_for_bytes
21
+ from agent_evolve.domain.event import (
22
+ ArtifactRegistered,
23
+ EventEnvelope,
24
+ LLMCallCompleted,
25
+ LLMCallFailed,
26
+ LLMCallRequested,
27
+ LLMCallRetried,
28
+ LLMCallStarted,
29
+ LLMRequestArtifactLinked,
30
+ ProviderAttemptCompleted,
31
+ ProviderAttemptFailed,
32
+ ProviderAttemptStarted,
33
+ validated_event_snapshot,
34
+ )
35
+ from agent_evolve.domain.ids import EventId, LLMCallId, ProviderAttemptId, RunId
36
+ from agent_evolve.domain.inline_text import InlineTextPolicy, validate_inline_text
37
+ from agent_evolve.domain.outcome import (
38
+ FailureCategory,
39
+ FailureCode,
40
+ validate_failure_pair,
41
+ )
42
+ from agent_evolve.ports.artifact_store import (
43
+ JSON_MEDIA_TYPE,
44
+ ArtifactStore,
45
+ canonical_json_bytes,
46
+ decode_json_bytes,
47
+ )
48
+
49
+ _READ_FAILED = object()
50
+ _STREAM_FAILED = object()
51
+ _CLONE_FAILED = object()
52
+ _BUILD_TOKEN = object()
53
+
54
+
55
+ class RecordedProviderReplayError(RuntimeError):
56
+ """Recorded provider history is missing, inconsistent, or not replayable."""
57
+
58
+
59
+ class RecordedCallNotFoundError(RecordedProviderReplayError):
60
+ pass
61
+
62
+
63
+ class RecordedRequestMismatchError(RecordedProviderReplayError):
64
+ pass
65
+
66
+
67
+ class RecordedCallUnavailableError(RecordedProviderReplayError):
68
+ pass
69
+
70
+
71
+ class RecordedCallStatus(str, Enum):
72
+ INCOMPLETE = "incomplete"
73
+ COMPLETED = "completed"
74
+ FAILED = "failed"
75
+
76
+
77
+ class RecordedAttemptStatus(str, Enum):
78
+ STARTED = "started"
79
+ COMPLETED = "completed"
80
+ FAILED = "failed"
81
+
82
+
83
+ @dataclass(frozen=True, slots=True)
84
+ class _RegistrationOccurrence:
85
+ sequence_number: int
86
+ causation_event_id: Optional[EventId]
87
+
88
+
89
+ @dataclass(frozen=True, slots=True)
90
+ class RecordedProviderAttempt:
91
+ provider_attempt_id: ProviderAttemptId
92
+ attempt_number: int
93
+ status: RecordedAttemptStatus
94
+ resolved_provider: Optional[str] = None
95
+ resolved_model: Optional[str] = None
96
+ input_tokens: int = 0
97
+ output_tokens: int = 0
98
+ reasoning_tokens: int = 0
99
+ cache_read_tokens: int = 0
100
+ cache_write_tokens: int = 0
101
+ cost_usd: Decimal = Decimal("0")
102
+ latency_ns: int = 0
103
+ failure_category: Optional[FailureCategory] = None
104
+ failure_code: Optional[FailureCode] = None
105
+ retryable: Optional[bool] = None
106
+ failure_message: Optional[str] = None
107
+ _construction_token: InitVar[object] = None
108
+
109
+ def __post_init__(self, _construction_token: object) -> None:
110
+ if _construction_token is not _BUILD_TOKEN:
111
+ raise TypeError("recorded attempts are constructed only by verified replay")
112
+ _validate_recorded_attempt(self)
113
+
114
+
115
+ @dataclass(frozen=True, slots=True)
116
+ class RecordedLLMCall:
117
+ call_id: LLMCallId
118
+ operation: str
119
+ requested_model: str
120
+ request_ref: ArtifactRef
121
+ request_bytes: bytes = field(repr=False)
122
+ status: RecordedCallStatus
123
+ attempts: Tuple[RecordedProviderAttempt, ...]
124
+ response_ref: Optional[ArtifactRef] = None
125
+ response_bytes: Optional[bytes] = field(default=None, repr=False)
126
+ failure_category: Optional[FailureCategory] = None
127
+ failure_code: Optional[FailureCode] = None
128
+ failure_message: Optional[str] = None
129
+ _construction_token: InitVar[object] = None
130
+
131
+ def __post_init__(self, _construction_token: object) -> None:
132
+ if _construction_token is not _BUILD_TOKEN:
133
+ raise TypeError("recorded calls are constructed only by verified replay")
134
+ _validate_recorded_call(self)
135
+
136
+
137
+ def _is_nonnegative_int(value: object) -> bool:
138
+ return type(value) is int and value >= 0
139
+
140
+
141
+ def _is_canonical_json_ref(ref: object, content: object) -> bool:
142
+ if type(ref) is not ArtifactRef or type(content) is not bytes:
143
+ return False
144
+ try:
145
+ reconstructed = artifact_ref_for_bytes(content, media_type=JSON_MEDIA_TYPE)
146
+ canonical = canonical_json_bytes(decode_json_bytes(content))
147
+ validated_ref = ArtifactRef(
148
+ artifact_id=ref.artifact_id,
149
+ sha256_hex=ref.sha256_hex,
150
+ size_bytes=ref.size_bytes,
151
+ media_type=ref.media_type,
152
+ )
153
+ except Exception:
154
+ return False
155
+ return canonical == content and validated_ref == reconstructed
156
+
157
+
158
+ def _is_safe_inline(value: object, policy: InlineTextPolicy) -> bool:
159
+ try:
160
+ validate_inline_text(value, field_name="recorded metadata", policy=policy)
161
+ except (TypeError, ValueError):
162
+ return False
163
+ return True
164
+
165
+
166
+ def _validate_recorded_attempt(attempt: RecordedProviderAttempt) -> None:
167
+ if (
168
+ type(attempt.provider_attempt_id) is not ProviderAttemptId
169
+ or type(attempt.attempt_number) is not int
170
+ or attempt.attempt_number < 1
171
+ or type(attempt.status) is not RecordedAttemptStatus
172
+ or any(
173
+ not _is_nonnegative_int(value)
174
+ for value in (
175
+ attempt.input_tokens,
176
+ attempt.output_tokens,
177
+ attempt.reasoning_tokens,
178
+ attempt.cache_read_tokens,
179
+ attempt.cache_write_tokens,
180
+ attempt.latency_ns,
181
+ )
182
+ )
183
+ or type(attempt.cost_usd) is not Decimal
184
+ or not attempt.cost_usd.is_finite()
185
+ or attempt.cost_usd < 0
186
+ or len(attempt.cost_usd.as_tuple().digits) > 64
187
+ or abs(attempt.cost_usd.as_tuple().exponent) > 64
188
+ ):
189
+ raise TypeError("recorded attempt violates the immutable value contract")
190
+
191
+ if attempt.status is RecordedAttemptStatus.STARTED:
192
+ if (
193
+ attempt.resolved_provider is not None
194
+ or attempt.resolved_model is not None
195
+ or any(
196
+ value != 0
197
+ for value in (
198
+ attempt.input_tokens,
199
+ attempt.output_tokens,
200
+ attempt.reasoning_tokens,
201
+ attempt.cache_read_tokens,
202
+ attempt.cache_write_tokens,
203
+ attempt.cost_usd,
204
+ attempt.latency_ns,
205
+ )
206
+ )
207
+ or attempt.failure_category is not None
208
+ or attempt.failure_code is not None
209
+ or attempt.retryable is not None
210
+ or attempt.failure_message is not None
211
+ ):
212
+ raise ValueError("started recorded attempt carries terminal fields")
213
+ return
214
+
215
+ if attempt.status is RecordedAttemptStatus.COMPLETED:
216
+ if (
217
+ not _is_safe_inline(
218
+ attempt.resolved_provider,
219
+ InlineTextPolicy.ROUTING_LABEL,
220
+ )
221
+ or not _is_safe_inline(
222
+ attempt.resolved_model,
223
+ InlineTextPolicy.ROUTING_LABEL,
224
+ )
225
+ or attempt.failure_category is not None
226
+ or attempt.failure_code is not None
227
+ or attempt.retryable is not None
228
+ or attempt.failure_message is not None
229
+ ):
230
+ raise ValueError("completed recorded attempt has inconsistent fields")
231
+ return
232
+
233
+ if (
234
+ attempt.resolved_provider is not None
235
+ or attempt.resolved_model is not None
236
+ or any(
237
+ value != 0
238
+ for value in (
239
+ attempt.input_tokens,
240
+ attempt.output_tokens,
241
+ attempt.reasoning_tokens,
242
+ attempt.cache_read_tokens,
243
+ attempt.cache_write_tokens,
244
+ attempt.cost_usd,
245
+ )
246
+ )
247
+ or type(attempt.failure_category) is not FailureCategory
248
+ or type(attempt.failure_code) is not FailureCode
249
+ or type(attempt.retryable) is not bool
250
+ or not _is_safe_inline(
251
+ attempt.failure_message,
252
+ InlineTextPolicy.SAFE_SUMMARY,
253
+ )
254
+ ):
255
+ raise ValueError("failed recorded attempt has inconsistent fields")
256
+ validate_failure_pair(attempt.failure_category, attempt.failure_code)
257
+
258
+
259
+ def _validate_recorded_call(call: RecordedLLMCall) -> None:
260
+ if (
261
+ type(call.call_id) is not LLMCallId
262
+ or not _is_safe_inline(call.operation, InlineTextPolicy.METADATA_TOKEN)
263
+ or not _is_safe_inline(call.requested_model, InlineTextPolicy.ROUTING_LABEL)
264
+ or not _is_canonical_json_ref(call.request_ref, call.request_bytes)
265
+ or type(call.status) is not RecordedCallStatus
266
+ or type(call.attempts) is not tuple
267
+ or any(type(attempt) is not RecordedProviderAttempt for attempt in call.attempts)
268
+ or [attempt.attempt_number for attempt in call.attempts]
269
+ != list(range(1, len(call.attempts) + 1))
270
+ or len({attempt.provider_attempt_id for attempt in call.attempts})
271
+ != len(call.attempts)
272
+ ):
273
+ raise TypeError("recorded call violates the immutable value contract")
274
+
275
+ if call.status is RecordedCallStatus.COMPLETED:
276
+ if (
277
+ not call.attempts
278
+ or call.attempts[-1].status is not RecordedAttemptStatus.COMPLETED
279
+ or not _is_canonical_json_ref(call.response_ref, call.response_bytes)
280
+ or call.failure_category is not None
281
+ or call.failure_code is not None
282
+ or call.failure_message is not None
283
+ ):
284
+ raise ValueError("completed recorded call has inconsistent fields")
285
+ return
286
+
287
+ if call.response_ref is not None or call.response_bytes is not None:
288
+ raise ValueError("non-completed recorded call cannot carry a response")
289
+ if call.status is RecordedCallStatus.INCOMPLETE:
290
+ if (
291
+ call.failure_category is not None
292
+ or call.failure_code is not None
293
+ or call.failure_message is not None
294
+ ):
295
+ raise ValueError("incomplete recorded call cannot carry failure fields")
296
+ return
297
+ if (
298
+ type(call.failure_category) is not FailureCategory
299
+ or type(call.failure_code) is not FailureCode
300
+ or not _is_safe_inline(call.failure_message, InlineTextPolicy.SAFE_SUMMARY)
301
+ ):
302
+ raise ValueError("failed recorded call has inconsistent fields")
303
+ validate_failure_pair(call.failure_category, call.failure_code)
304
+
305
+
306
+ def _clone_recorded_attempt(
307
+ attempt: RecordedProviderAttempt,
308
+ ) -> RecordedProviderAttempt:
309
+ return RecordedProviderAttempt(
310
+ provider_attempt_id=ProviderAttemptId(attempt.provider_attempt_id.value),
311
+ attempt_number=attempt.attempt_number,
312
+ status=attempt.status,
313
+ resolved_provider=attempt.resolved_provider,
314
+ resolved_model=attempt.resolved_model,
315
+ input_tokens=attempt.input_tokens,
316
+ output_tokens=attempt.output_tokens,
317
+ reasoning_tokens=attempt.reasoning_tokens,
318
+ cache_read_tokens=attempt.cache_read_tokens,
319
+ cache_write_tokens=attempt.cache_write_tokens,
320
+ cost_usd=Decimal(attempt.cost_usd),
321
+ latency_ns=attempt.latency_ns,
322
+ failure_category=attempt.failure_category,
323
+ failure_code=attempt.failure_code,
324
+ retryable=attempt.retryable,
325
+ failure_message=attempt.failure_message,
326
+ _construction_token=_BUILD_TOKEN,
327
+ )
328
+
329
+
330
+ def _clone_recorded_call(call: RecordedLLMCall) -> RecordedLLMCall:
331
+ request_bytes = bytes(call.request_bytes)
332
+ response_bytes = (
333
+ bytes(call.response_bytes) if call.response_bytes is not None else None
334
+ )
335
+ return RecordedLLMCall(
336
+ call_id=LLMCallId(call.call_id.value),
337
+ operation=call.operation,
338
+ requested_model=call.requested_model,
339
+ request_ref=artifact_ref_for_bytes(
340
+ request_bytes,
341
+ media_type=JSON_MEDIA_TYPE,
342
+ ),
343
+ request_bytes=request_bytes,
344
+ status=call.status,
345
+ attempts=tuple(_clone_recorded_attempt(item) for item in call.attempts),
346
+ response_ref=(
347
+ artifact_ref_for_bytes(response_bytes, media_type=JSON_MEDIA_TYPE)
348
+ if response_bytes is not None
349
+ else None
350
+ ),
351
+ response_bytes=response_bytes,
352
+ failure_category=call.failure_category,
353
+ failure_code=call.failure_code,
354
+ failure_message=call.failure_message,
355
+ _construction_token=_BUILD_TOKEN,
356
+ )
357
+
358
+
359
+ def _try_clone_recorded_call(call: RecordedLLMCall) -> RecordedLLMCall | object:
360
+ try:
361
+ _validate_recorded_call(call)
362
+ return _clone_recorded_call(call)
363
+ except Exception:
364
+ return _CLONE_FAILED
365
+
366
+
367
+ def _recorded_call_fingerprint(call: RecordedLLMCall) -> str:
368
+ record = {
369
+ "call_id": call.call_id.value,
370
+ "operation": call.operation,
371
+ "requested_model": call.requested_model,
372
+ "request_sha256": hashlib.sha256(call.request_bytes).hexdigest(),
373
+ "status": call.status.value,
374
+ "attempts": [
375
+ {
376
+ "provider_attempt_id": attempt.provider_attempt_id.value,
377
+ "attempt_number": attempt.attempt_number,
378
+ "status": attempt.status.value,
379
+ "resolved_provider": attempt.resolved_provider,
380
+ "resolved_model": attempt.resolved_model,
381
+ "input_tokens": attempt.input_tokens,
382
+ "output_tokens": attempt.output_tokens,
383
+ "reasoning_tokens": attempt.reasoning_tokens,
384
+ "cache_read_tokens": attempt.cache_read_tokens,
385
+ "cache_write_tokens": attempt.cache_write_tokens,
386
+ "cost_usd": format(attempt.cost_usd, "f"),
387
+ "latency_ns": attempt.latency_ns,
388
+ "failure_category": (
389
+ attempt.failure_category.value
390
+ if attempt.failure_category is not None
391
+ else None
392
+ ),
393
+ "failure_code": (
394
+ attempt.failure_code.value
395
+ if attempt.failure_code is not None
396
+ else None
397
+ ),
398
+ "retryable": attempt.retryable,
399
+ "failure_message": attempt.failure_message,
400
+ }
401
+ for attempt in call.attempts
402
+ ],
403
+ "response_sha256": (
404
+ hashlib.sha256(call.response_bytes).hexdigest()
405
+ if call.response_bytes is not None
406
+ else None
407
+ ),
408
+ "failure_category": (
409
+ call.failure_category.value
410
+ if call.failure_category is not None
411
+ else None
412
+ ),
413
+ "failure_code": (
414
+ call.failure_code.value if call.failure_code is not None else None
415
+ ),
416
+ "failure_message": call.failure_message,
417
+ }
418
+ return hashlib.sha256(canonical_json_bytes(record)).hexdigest()
419
+
420
+
421
+ @dataclass(slots=True)
422
+ class _AttemptBuilder:
423
+ provider_attempt_id: ProviderAttemptId
424
+ attempt_number: int
425
+ status: RecordedAttemptStatus = RecordedAttemptStatus.STARTED
426
+ resolved_provider: Optional[str] = None
427
+ resolved_model: Optional[str] = None
428
+ input_tokens: int = 0
429
+ output_tokens: int = 0
430
+ reasoning_tokens: int = 0
431
+ cache_read_tokens: int = 0
432
+ cache_write_tokens: int = 0
433
+ cost_usd: Decimal = Decimal("0")
434
+ latency_ns: int = 0
435
+ failure_category: Optional[FailureCategory] = None
436
+ failure_code: Optional[FailureCode] = None
437
+ retryable: Optional[bool] = None
438
+ failure_message: Optional[str] = None
439
+ terminal_sequence: Optional[int] = None
440
+ terminal_event_id: Optional[EventId] = None
441
+
442
+ def freeze(self) -> RecordedProviderAttempt:
443
+ return RecordedProviderAttempt(
444
+ provider_attempt_id=self.provider_attempt_id,
445
+ attempt_number=self.attempt_number,
446
+ status=self.status,
447
+ resolved_provider=self.resolved_provider,
448
+ resolved_model=self.resolved_model,
449
+ input_tokens=self.input_tokens,
450
+ output_tokens=self.output_tokens,
451
+ reasoning_tokens=self.reasoning_tokens,
452
+ cache_read_tokens=self.cache_read_tokens,
453
+ cache_write_tokens=self.cache_write_tokens,
454
+ cost_usd=self.cost_usd,
455
+ latency_ns=self.latency_ns,
456
+ failure_category=self.failure_category,
457
+ failure_code=self.failure_code,
458
+ retryable=self.retryable,
459
+ failure_message=self.failure_message,
460
+ _construction_token=_BUILD_TOKEN,
461
+ )
462
+
463
+
464
+ @dataclass(slots=True)
465
+ class _CallBuilder:
466
+ call_id: LLMCallId
467
+ operation: str
468
+ requested_model: str
469
+ requested_sequence: int
470
+ request_ref: Optional[ArtifactRef] = None
471
+ request_bytes: Optional[bytes] = None
472
+ started: bool = False
473
+ status: RecordedCallStatus = RecordedCallStatus.INCOMPLETE
474
+ attempts: list[_AttemptBuilder] = field(default_factory=list)
475
+ open_attempt_id: Optional[ProviderAttemptId] = None
476
+ authorized_retry_number: Optional[int] = None
477
+ response_ref: Optional[ArtifactRef] = None
478
+ response_bytes: Optional[bytes] = None
479
+ failure_category: Optional[FailureCategory] = None
480
+ failure_code: Optional[FailureCode] = None
481
+ failure_message: Optional[str] = None
482
+
483
+ @property
484
+ def terminal(self) -> bool:
485
+ return self.status is not RecordedCallStatus.INCOMPLETE
486
+
487
+
488
+ def _fail() -> RecordedProviderReplayError:
489
+ return RecordedProviderReplayError(
490
+ "recorded provider history violates the offline replay contract"
491
+ )
492
+
493
+
494
+ def _try_snapshot_event_stream(
495
+ events: Iterable[EventEnvelope],
496
+ ) -> Tuple[EventEnvelope, ...] | object:
497
+ try:
498
+ return tuple(validated_event_snapshot(event) for event in events)
499
+ except Exception:
500
+ return _STREAM_FAILED
501
+
502
+
503
+ @dataclass(frozen=True, slots=True)
504
+ class RecordedProviderReplay:
505
+ """Immutable offline provider boundary keyed by recorded logical call ID."""
506
+
507
+ run_id: Optional[RunId]
508
+ calls: Tuple[RecordedLLMCall, ...] = field(repr=False)
509
+ _by_call_id: Mapping[LLMCallId, RecordedLLMCall] = field(
510
+ init=False,
511
+ repr=False,
512
+ compare=False,
513
+ )
514
+ _fingerprints: Mapping[LLMCallId, str] = field(
515
+ init=False,
516
+ repr=False,
517
+ compare=False,
518
+ )
519
+ _construction_token: InitVar[object] = None
520
+
521
+ def __post_init__(self, _construction_token: object) -> None:
522
+ if _construction_token is not _BUILD_TOKEN:
523
+ raise TypeError("recorded replay is constructed only by verified replay")
524
+ if type(self.calls) is not tuple:
525
+ raise TypeError("calls must be an immutable tuple")
526
+ if self.run_id is not None and type(self.run_id) is not RunId:
527
+ raise TypeError("run_id must be a RunId or None")
528
+ if any(type(call) is not RecordedLLMCall for call in self.calls):
529
+ raise TypeError("calls must contain RecordedLLMCall values")
530
+ try:
531
+ public_calls = tuple(_try_clone_recorded_call(call) for call in self.calls)
532
+ internal_calls = tuple(_try_clone_recorded_call(call) for call in self.calls)
533
+ canonical_run_id = (
534
+ RunId(self.run_id.value) if self.run_id is not None else None
535
+ )
536
+ except Exception:
537
+ public_calls = None
538
+ internal_calls = None
539
+ canonical_run_id = None
540
+ if public_calls is None or internal_calls is None:
541
+ raise TypeError("recorded replay inputs failed canonical reconstruction")
542
+ if any(call is _CLONE_FAILED for call in public_calls + internal_calls):
543
+ raise TypeError("recorded replay inputs failed canonical reconstruction")
544
+ assert all(isinstance(call, RecordedLLMCall) for call in public_calls)
545
+ assert all(isinstance(call, RecordedLLMCall) for call in internal_calls)
546
+ by_call_id = {call.call_id: call for call in internal_calls}
547
+ if len(by_call_id) != len(internal_calls):
548
+ raise ValueError("recorded calls must have unique call IDs")
549
+ object.__setattr__(self, "run_id", canonical_run_id)
550
+ object.__setattr__(self, "calls", public_calls)
551
+ object.__setattr__(self, "_by_call_id", MappingProxyType(by_call_id))
552
+ object.__setattr__(
553
+ self,
554
+ "_fingerprints",
555
+ MappingProxyType(
556
+ {
557
+ call_id: _recorded_call_fingerprint(call)
558
+ for call_id, call in by_call_id.items()
559
+ }
560
+ ),
561
+ )
562
+
563
+ def _internal_call(self, call_id: LLMCallId) -> RecordedLLMCall:
564
+ if type(call_id) is not LLMCallId:
565
+ raise TypeError("call_id must be an LLMCallId")
566
+ try:
567
+ canonical_call_id = LLMCallId(call_id.value)
568
+ call = self._by_call_id[canonical_call_id]
569
+ except (KeyError, TypeError, ValueError):
570
+ raise RecordedCallNotFoundError(
571
+ "recorded logical call was not found"
572
+ ) from None
573
+ validated = _try_clone_recorded_call(call)
574
+ if (
575
+ validated is _CLONE_FAILED
576
+ or _recorded_call_fingerprint(call)
577
+ != self._fingerprints.get(canonical_call_id)
578
+ ):
579
+ raise RecordedProviderReplayError(
580
+ "recorded replay snapshot failed its immutable integrity check"
581
+ )
582
+ return call
583
+
584
+ def call(self, call_id: LLMCallId) -> RecordedLLMCall:
585
+ call = _try_clone_recorded_call(self._internal_call(call_id))
586
+ if call is _CLONE_FAILED: # pragma: no cover - guarded by _internal_call.
587
+ raise RecordedProviderReplayError(
588
+ "recorded replay snapshot failed its immutable integrity check"
589
+ )
590
+ assert isinstance(call, RecordedLLMCall)
591
+ return call
592
+
593
+ def replay_response(self, call_id: LLMCallId, *, request_bytes: bytes) -> bytes:
594
+ """Return exact recorded response bytes only for the exact recorded request."""
595
+
596
+ if type(request_bytes) is not bytes:
597
+ raise TypeError("request_bytes must be immutable bytes")
598
+ call = self._internal_call(call_id)
599
+ if request_bytes != call.request_bytes:
600
+ raise RecordedRequestMismatchError(
601
+ "supplied request does not match the recorded sanitized request"
602
+ )
603
+ if (
604
+ call.status is not RecordedCallStatus.COMPLETED
605
+ or call.response_bytes is None
606
+ ):
607
+ raise RecordedCallUnavailableError(
608
+ "recorded call has no completed response available for replay"
609
+ )
610
+ return call.response_bytes
611
+
612
+
613
+ def _read_registered_bytes(
614
+ store: ArtifactStore,
615
+ registrations: Dict[
616
+ ArtifactRegistrationKey,
617
+ tuple[ArtifactRef, list[_RegistrationOccurrence]],
618
+ ],
619
+ artifact_id,
620
+ role: ArtifactRole,
621
+ ) -> tuple[ArtifactRef, bytes, Tuple[_RegistrationOccurrence, ...]]:
622
+ key = ArtifactRegistrationKey(artifact_id, role)
623
+ registered = registrations.get(key)
624
+ if registered is None:
625
+ raise _fail()
626
+ ref, registration_occurrences = registered
627
+ content = _try_read_canonical_registered_bytes(store, ref)
628
+ if content is _READ_FAILED:
629
+ raise _fail()
630
+ assert isinstance(content, bytes)
631
+ return ref, content, tuple(registration_occurrences)
632
+
633
+
634
+ def _try_read_canonical_registered_bytes(
635
+ store: ArtifactStore,
636
+ ref: ArtifactRef,
637
+ ) -> bytes | object:
638
+ try:
639
+ content = store.read_bytes(
640
+ ref.artifact_id,
641
+ expected_media_type=JSON_MEDIA_TYPE,
642
+ )
643
+ reread_ref = artifact_ref_for_bytes(content, media_type=JSON_MEDIA_TYPE)
644
+ canonical = canonical_json_bytes(decode_json_bytes(content))
645
+ if reread_ref != ref or canonical != content:
646
+ return _READ_FAILED
647
+ return content
648
+ except Exception:
649
+ # Return from the exception handler so the public replay error cannot
650
+ # retain secret-bearing store/codec exception context.
651
+ return _READ_FAILED
652
+
653
+
654
+ def _require_call(
655
+ calls: Dict[LLMCallId, _CallBuilder],
656
+ call_id: LLMCallId,
657
+ ) -> _CallBuilder:
658
+ call = calls.get(call_id)
659
+ if call is None:
660
+ raise _fail()
661
+ return call
662
+
663
+
664
+ def build_recorded_provider_replay(
665
+ events: Iterable[EventEnvelope],
666
+ *,
667
+ artifact_store: ArtifactStore,
668
+ ) -> RecordedProviderReplay:
669
+ """Validate and reconstruct an offline replay boundary from one run stream."""
670
+
671
+ stream = _try_snapshot_event_stream(events)
672
+ if stream is _STREAM_FAILED:
673
+ raise _fail()
674
+ assert isinstance(stream, tuple)
675
+ report = verify_artifact_journal(stream, artifact_store=artifact_store)
676
+ registrations: Dict[
677
+ ArtifactRegistrationKey,
678
+ tuple[ArtifactRef, list[_RegistrationOccurrence]],
679
+ ] = {}
680
+ calls: Dict[LLMCallId, _CallBuilder] = {}
681
+ call_order: list[LLMCallId] = []
682
+ attempts_by_id: Dict[ProviderAttemptId, tuple[LLMCallId, _AttemptBuilder]] = {}
683
+ seen_event_ids: set[EventId] = set()
684
+
685
+ for event in stream:
686
+ if event.event_id in seen_event_ids or (
687
+ event.causation_event_id is not None
688
+ and event.causation_event_id not in seen_event_ids
689
+ ):
690
+ raise _fail()
691
+ seen_event_ids.add(event.event_id)
692
+ payload = event.payload
693
+ if isinstance(payload, ArtifactRegistered):
694
+ registration_key = ArtifactRegistrationKey(
695
+ payload.artifact_id,
696
+ payload.role,
697
+ )
698
+ registered = registrations.get(registration_key)
699
+ if registered is None:
700
+ registrations[registration_key] = (
701
+ payload.artifact_ref,
702
+ [
703
+ _RegistrationOccurrence(
704
+ sequence_number=event.sequence_number,
705
+ causation_event_id=event.causation_event_id,
706
+ )
707
+ ],
708
+ )
709
+ else:
710
+ registered[1].append(
711
+ _RegistrationOccurrence(
712
+ sequence_number=event.sequence_number,
713
+ causation_event_id=event.causation_event_id,
714
+ )
715
+ )
716
+ continue
717
+
718
+ if isinstance(payload, LLMCallRequested):
719
+ if payload.call_id in calls:
720
+ raise _fail()
721
+ calls[payload.call_id] = _CallBuilder(
722
+ call_id=payload.call_id,
723
+ operation=payload.operation,
724
+ requested_model=payload.requested_model,
725
+ requested_sequence=event.sequence_number,
726
+ )
727
+ call_order.append(payload.call_id)
728
+ continue
729
+
730
+ if isinstance(payload, LLMRequestArtifactLinked):
731
+ call = _require_call(calls, payload.call_id)
732
+ if call.request_ref is not None or call.started or call.terminal:
733
+ raise _fail()
734
+ (
735
+ call.request_ref,
736
+ call.request_bytes,
737
+ registration_occurrences,
738
+ ) = _read_registered_bytes(
739
+ artifact_store,
740
+ registrations,
741
+ payload.request_artifact_id,
742
+ ArtifactRole.LLM_REQUEST,
743
+ )
744
+ if not any(
745
+ occurrence.sequence_number < call.requested_sequence
746
+ for occurrence in registration_occurrences
747
+ ):
748
+ raise _fail()
749
+ continue
750
+
751
+ if isinstance(payload, LLMCallStarted):
752
+ call = _require_call(calls, payload.call_id)
753
+ if call.started or call.terminal or call.request_ref is None:
754
+ raise _fail()
755
+ call.started = True
756
+ continue
757
+
758
+ if isinstance(payload, ProviderAttemptStarted):
759
+ call = _require_call(calls, payload.call_id)
760
+ expected_number = len(call.attempts) + 1
761
+ if (
762
+ not call.started
763
+ or call.terminal
764
+ or call.open_attempt_id is not None
765
+ or payload.provider_attempt_id in attempts_by_id
766
+ or payload.attempt_number != expected_number
767
+ or (
768
+ expected_number > 1
769
+ and call.authorized_retry_number != expected_number
770
+ )
771
+ ):
772
+ raise _fail()
773
+ attempt = _AttemptBuilder(
774
+ provider_attempt_id=payload.provider_attempt_id,
775
+ attempt_number=payload.attempt_number,
776
+ )
777
+ call.attempts.append(attempt)
778
+ call.open_attempt_id = payload.provider_attempt_id
779
+ call.authorized_retry_number = None
780
+ attempts_by_id[payload.provider_attempt_id] = (payload.call_id, attempt)
781
+ continue
782
+
783
+ if isinstance(payload, (ProviderAttemptCompleted, ProviderAttemptFailed)):
784
+ call = _require_call(calls, payload.call_id)
785
+ found = attempts_by_id.get(payload.provider_attempt_id)
786
+ if (
787
+ found is None
788
+ or found[0] != payload.call_id
789
+ or call.open_attempt_id != payload.provider_attempt_id
790
+ or call.terminal
791
+ or found[1].status is not RecordedAttemptStatus.STARTED
792
+ ):
793
+ raise _fail()
794
+ attempt = found[1]
795
+ if isinstance(payload, ProviderAttemptCompleted):
796
+ attempt.status = RecordedAttemptStatus.COMPLETED
797
+ attempt.resolved_provider = payload.resolved_provider
798
+ attempt.resolved_model = payload.resolved_model
799
+ attempt.input_tokens = payload.input_tokens
800
+ attempt.output_tokens = payload.output_tokens
801
+ attempt.reasoning_tokens = payload.reasoning_tokens
802
+ attempt.cache_read_tokens = payload.cache_read_tokens
803
+ attempt.cache_write_tokens = payload.cache_write_tokens
804
+ attempt.cost_usd = payload.cost_usd
805
+ attempt.latency_ns = payload.latency_ns
806
+ else:
807
+ attempt.status = RecordedAttemptStatus.FAILED
808
+ attempt.failure_category = payload.category
809
+ attempt.failure_code = payload.code
810
+ attempt.retryable = payload.retryable
811
+ attempt.failure_message = payload.message
812
+ attempt.latency_ns = payload.latency_ns
813
+ attempt.terminal_sequence = event.sequence_number
814
+ attempt.terminal_event_id = event.event_id
815
+ call.open_attempt_id = None
816
+ continue
817
+
818
+ if isinstance(payload, LLMCallRetried):
819
+ call = _require_call(calls, payload.call_id)
820
+ if (
821
+ not call.started
822
+ or call.terminal
823
+ or call.open_attempt_id is not None
824
+ or not call.attempts
825
+ or call.attempts[-1].status is not RecordedAttemptStatus.FAILED
826
+ or not call.attempts[-1].retryable
827
+ or call.authorized_retry_number is not None
828
+ or payload.next_attempt_number != len(call.attempts) + 1
829
+ ):
830
+ raise _fail()
831
+ call.authorized_retry_number = payload.next_attempt_number
832
+ continue
833
+
834
+ if isinstance(payload, LLMCallCompleted):
835
+ call = _require_call(calls, payload.call_id)
836
+ if (
837
+ not call.started
838
+ or call.terminal
839
+ or call.open_attempt_id is not None
840
+ or not call.attempts
841
+ or call.attempts[-1].status is not RecordedAttemptStatus.COMPLETED
842
+ or payload.response_artifact_id is None
843
+ ):
844
+ raise _fail()
845
+ (
846
+ call.response_ref,
847
+ call.response_bytes,
848
+ registration_occurrences,
849
+ ) = _read_registered_bytes(
850
+ artifact_store,
851
+ registrations,
852
+ payload.response_artifact_id,
853
+ ArtifactRole.LLM_RESPONSE,
854
+ )
855
+ if (
856
+ call.attempts[-1].terminal_sequence is None
857
+ or call.attempts[-1].terminal_event_id is None
858
+ or not any(
859
+ occurrence.sequence_number
860
+ > call.attempts[-1].terminal_sequence
861
+ and occurrence.causation_event_id
862
+ == call.attempts[-1].terminal_event_id
863
+ for occurrence in registration_occurrences
864
+ )
865
+ ):
866
+ raise _fail()
867
+ call.status = RecordedCallStatus.COMPLETED
868
+ continue
869
+
870
+ if isinstance(payload, LLMCallFailed):
871
+ call = _require_call(calls, payload.call_id)
872
+ if (
873
+ not call.started
874
+ or call.terminal
875
+ or call.open_attempt_id is not None
876
+ or call.authorized_retry_number is not None
877
+ ):
878
+ raise _fail()
879
+ call.status = RecordedCallStatus.FAILED
880
+ call.failure_category = payload.category
881
+ call.failure_code = payload.code
882
+ call.failure_message = payload.message
883
+
884
+ frozen_calls: list[RecordedLLMCall] = []
885
+ for call_id in call_order:
886
+ call = calls[call_id]
887
+ if call.request_ref is None or call.request_bytes is None:
888
+ raise _fail()
889
+ frozen_calls.append(
890
+ RecordedLLMCall(
891
+ call_id=call.call_id,
892
+ operation=call.operation,
893
+ requested_model=call.requested_model,
894
+ request_ref=call.request_ref,
895
+ request_bytes=call.request_bytes,
896
+ status=call.status,
897
+ attempts=tuple(attempt.freeze() for attempt in call.attempts),
898
+ response_ref=call.response_ref,
899
+ response_bytes=call.response_bytes,
900
+ failure_category=call.failure_category,
901
+ failure_code=call.failure_code,
902
+ failure_message=call.failure_message,
903
+ _construction_token=_BUILD_TOKEN,
904
+ )
905
+ )
906
+ return RecordedProviderReplay(
907
+ run_id=report.run_id,
908
+ calls=tuple(frozen_calls),
909
+ _construction_token=_BUILD_TOKEN,
910
+ )