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,960 @@
1
+ """Provider-neutral values for asynchronous LLM task scheduling.
2
+
3
+ The values in this module contain no provider request, SDK exception, URL, or
4
+ transport type. Provider integrations classify their own exceptions through
5
+ the port defined in :mod:`agent_evolve.ports.llm_task_queue`.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import math
11
+ import re
12
+ from dataclasses import dataclass
13
+ from datetime import datetime, timezone
14
+ from email.utils import parsedate_to_datetime
15
+ from enum import Enum
16
+ from typing import Generic, Optional, Tuple, TypeVar
17
+
18
+ from agent_evolve.domain.ids import ProviderAttemptId
19
+
20
+
21
+ RequestT = TypeVar("RequestT")
22
+ ResponseT = TypeVar("ResponseT")
23
+
24
+ NANOSECONDS_PER_SECOND = 1_000_000_000
25
+ MAX_ATTEMPTS = 100
26
+ MAX_TASK_ID_LENGTH = 128
27
+ _TASK_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
28
+ _RETRY_AFTER_SECONDS = re.compile(r"^(?:0|[1-9][0-9]*)$")
29
+ _FAILURE_KIND = re.compile(r"^[a-z][a-z0-9_]{0,63}$")
30
+ _VALIDATION_LOCATION_TOKEN = re.compile(r"^[A-Za-z][A-Za-z0-9_-]{0,63}$")
31
+ _SHA256 = re.compile(r"^[0-9a-f]{64}$")
32
+ MAX_VALIDATION_ISSUES = 8
33
+ MAX_VALIDATION_LOCATION_DEPTH = 8
34
+ MAX_EXCEPTION_PROVENANCE_NODES = 16
35
+
36
+
37
+ def _require_nonnegative_ns(value: object, name: str) -> None:
38
+ if type(value) is not int or value < 0:
39
+ raise ValueError(f"{name} must be a non-negative integer number of nanoseconds")
40
+
41
+
42
+ def _require_nonnegative_int(value: object, name: str) -> None:
43
+ if type(value) is not int or value < 0:
44
+ raise ValueError(f"{name} must be a non-negative integer")
45
+
46
+
47
+ def _require_positive_ns(value: object, name: str) -> None:
48
+ if type(value) is not int or value <= 0:
49
+ raise ValueError(f"{name} must be a positive integer number of nanoseconds")
50
+
51
+
52
+ class RetryDisposition(str, Enum):
53
+ RETRY = "retry"
54
+ FAIL = "fail"
55
+
56
+
57
+ class RetryReason(str, Enum):
58
+ RATE_LIMIT = "rate_limit"
59
+ TRANSIENT = "transient"
60
+ TIMEOUT = "timeout"
61
+ OUTPUT_INVALID = "output_invalid"
62
+ PERMANENT = "permanent"
63
+ INTERNAL = "internal"
64
+
65
+
66
+ class RetryBudgetPartition(str, Enum):
67
+ """Independent retry allowances beneath the hard physical-attempt cap."""
68
+
69
+ OUTPUT_INVALID = "output_invalid"
70
+ TRANSPORT = "transport"
71
+
72
+
73
+ def retry_budget_partition(reason: RetryReason) -> RetryBudgetPartition:
74
+ """Map every retryable reason to its provider-neutral budget partition.
75
+
76
+ ``PERMANENT`` and ``INTERNAL`` classifications are terminal by definition;
77
+ admitting either as a retry would hide a broken classifier, so this helper
78
+ rejects them instead of silently charging an unrelated allowance.
79
+ """
80
+
81
+ if type(reason) is not RetryReason:
82
+ raise TypeError("reason must be a RetryReason")
83
+ if reason is RetryReason.OUTPUT_INVALID:
84
+ return RetryBudgetPartition.OUTPUT_INVALID
85
+ if reason in {
86
+ RetryReason.RATE_LIMIT,
87
+ RetryReason.TRANSIENT,
88
+ RetryReason.TIMEOUT,
89
+ }:
90
+ return RetryBudgetPartition.TRANSPORT
91
+ raise ValueError(f"{reason.value} is not a retryable budget reason")
92
+
93
+
94
+ @dataclass(frozen=True, slots=True)
95
+ class PartitionedRetryBudget:
96
+ """Bound semantic repair and transport replay independently.
97
+
98
+ Values count *additional attempts admitted after failures*, while
99
+ :attr:`LLMTask.max_attempts` remains the final bound on physical calls.
100
+ This prevents a transient provider failure from consuming the allowance
101
+ reserved for repairing invalid structured output, and vice versa.
102
+ """
103
+
104
+ output_invalid_retries: int
105
+ transport_retries: int
106
+
107
+ def __post_init__(self) -> None:
108
+ for name, value in (
109
+ ("output_invalid_retries", self.output_invalid_retries),
110
+ ("transport_retries", self.transport_retries),
111
+ ):
112
+ _require_nonnegative_int(value, name)
113
+ if value >= MAX_ATTEMPTS:
114
+ raise ValueError(f"{name} must be less than {MAX_ATTEMPTS}")
115
+
116
+ def limit(self, partition: RetryBudgetPartition) -> int:
117
+ if type(partition) is not RetryBudgetPartition:
118
+ raise TypeError("partition must be a RetryBudgetPartition")
119
+ if partition is RetryBudgetPartition.OUTPUT_INVALID:
120
+ return self.output_invalid_retries
121
+ return self.transport_retries
122
+
123
+
124
+ @dataclass(frozen=True, slots=True)
125
+ class RetryBudgetUsage:
126
+ """Immutable count of retry admissions before one physical attempt."""
127
+
128
+ output_invalid_retries: int = 0
129
+ transport_retries: int = 0
130
+
131
+ def __post_init__(self) -> None:
132
+ for name, value in (
133
+ ("output_invalid_retries", self.output_invalid_retries),
134
+ ("transport_retries", self.transport_retries),
135
+ ):
136
+ _require_nonnegative_int(value, name)
137
+ if value >= MAX_ATTEMPTS:
138
+ raise ValueError(f"{name} must be less than {MAX_ATTEMPTS}")
139
+
140
+ def used(self, partition: RetryBudgetPartition) -> int:
141
+ if type(partition) is not RetryBudgetPartition:
142
+ raise TypeError("partition must be a RetryBudgetPartition")
143
+ if partition is RetryBudgetPartition.OUTPUT_INVALID:
144
+ return self.output_invalid_retries
145
+ return self.transport_retries
146
+
147
+ def consume(self, partition: RetryBudgetPartition) -> "RetryBudgetUsage":
148
+ if type(partition) is not RetryBudgetPartition:
149
+ raise TypeError("partition must be a RetryBudgetPartition")
150
+ if partition is RetryBudgetPartition.OUTPUT_INVALID:
151
+ return RetryBudgetUsage(
152
+ output_invalid_retries=self.output_invalid_retries + 1,
153
+ transport_retries=self.transport_retries,
154
+ )
155
+ return RetryBudgetUsage(
156
+ output_invalid_retries=self.output_invalid_retries,
157
+ transport_retries=self.transport_retries + 1,
158
+ )
159
+
160
+
161
+ class RetryAfterSource(str, Enum):
162
+ DELAY_SECONDS = "delay_seconds"
163
+ HTTP_DATE = "http_date"
164
+
165
+
166
+ class StructuredOutputFailureMode(str, Enum):
167
+ """Closed, non-content-bearing modes for typed-output failures."""
168
+
169
+ SCHEMA_VALIDATION = "schema_validation"
170
+ TYPED_OUTPUT_CONTRACT = "typed_output_contract"
171
+ INCOMPLETE_TOOL_CALL = "incomplete_tool_call"
172
+
173
+
174
+ class CanonicalProviderErrorCode(str, Enum):
175
+ """Closed provider error codes safe to retain in durable telemetry.
176
+
177
+ Provider adapters may admit a value only from a structured error field;
178
+ messages, response bodies, and free-form provider metadata are never
179
+ coerced into this enum. The deliberately finite vocabulary covers the
180
+ OpenAI-compatible error types currently exposed by OpenRouter and common
181
+ upstream providers while failing closed for an unfamiliar value.
182
+ """
183
+
184
+ # Current OpenRouter typed error vocabulary.
185
+ AUTHENTICATION = "authentication"
186
+ CONTENT_POLICY_VIOLATION = "content_policy_violation"
187
+ CONTEXT_LENGTH_EXCEEDED = "context_length_exceeded"
188
+ IMAGE_DOWNLOAD_FAILED = "image_download_failed"
189
+ IMAGE_NOT_FOUND = "image_not_found"
190
+ IMAGE_TOO_LARGE = "image_too_large"
191
+ IMAGE_TOO_SMALL = "image_too_small"
192
+ INVALID_IMAGE = "invalid_image"
193
+ INVALID_PROMPT = "invalid_prompt"
194
+ INVALID_REQUEST = "invalid_request"
195
+ MAX_TOKENS_EXCEEDED = "max_tokens_exceeded"
196
+ NOT_FOUND = "not_found"
197
+ PAYLOAD_TOO_LARGE = "payload_too_large"
198
+ PAYMENT_REQUIRED = "payment_required"
199
+ PERMISSION_DENIED = "permission_denied"
200
+ PRECONDITION_FAILED = "precondition_failed"
201
+ PROVIDER_OVERLOADED = "provider_overloaded"
202
+ PROVIDER_UNAVAILABLE = "provider_unavailable"
203
+ RATE_LIMIT_EXCEEDED = "rate_limit_exceeded"
204
+ REFUSAL = "refusal"
205
+ SERVER = "server"
206
+ STRING_TOO_LONG = "string_too_long"
207
+ TIMEOUT = "timeout"
208
+ TOKEN_LIMIT_EXCEEDED = "token_limit_exceeded"
209
+ UNMAPPED = "unmapped"
210
+ UNPROCESSABLE = "unprocessable"
211
+ UNSUPPORTED_IMAGE_FORMAT = "unsupported_image_format"
212
+
213
+ # OpenAI SDK and upstream compatibility codes observed on compatible APIs.
214
+ API_ERROR = "api_error"
215
+ AUTHENTICATION_ERROR = "authentication_error"
216
+ BAD_REQUEST = "bad_request"
217
+ CONFLICT_ERROR = "conflict_error"
218
+ CONTENT_FILTER_ERROR = "content_filter_error"
219
+ INSUFFICIENT_QUOTA = "insufficient_quota"
220
+ INTERNAL_SERVER_ERROR = "internal_server_error"
221
+ INVALID_REQUEST_ERROR = "invalid_request_error"
222
+ MODEL_NOT_FOUND = "model_not_found"
223
+ NOT_FOUND_ERROR = "not_found_error"
224
+ OVERLOADED_ERROR = "overloaded_error"
225
+ PERMISSION_ERROR = "permission_error"
226
+ PROVIDER_ERROR = "provider_error"
227
+ RATE_LIMIT_ERROR = "rate_limit_error"
228
+ REQUEST_TIMEOUT = "request_timeout"
229
+ SERVER_ERROR = "server_error"
230
+ SERVICE_UNAVAILABLE_ERROR = "service_unavailable_error"
231
+ TOOL_ERROR = "tool_error"
232
+ UNPROCESSABLE_ENTITY_ERROR = "unprocessable_entity_error"
233
+
234
+
235
+ class StreamTimeoutPhase(str, Enum):
236
+ """Closed progress-aware stream boundary that expired."""
237
+
238
+ FIRST_EVENT = "first_event"
239
+ IDLE = "idle"
240
+ ABSOLUTE = "absolute"
241
+
242
+
243
+ class ExceptionOriginFamily(str, Enum):
244
+ """Closed runtime families safe to expose in durable diagnostics.
245
+
246
+ Exact exception type identity is carried separately as a domain-separated
247
+ fingerprint. Keeping this value closed prevents an attacker-controlled
248
+ module or class name from becoming durable journal text.
249
+ """
250
+
251
+ BUILTINS = "builtins"
252
+ ASYNCIO = "asyncio"
253
+ ANYIO = "anyio"
254
+ HTTPX = "httpx"
255
+ HTTPCORE = "httpcore"
256
+ OPENAI = "openai"
257
+ PYDANTIC = "pydantic"
258
+ PYDANTIC_AI = "pydantic_ai"
259
+ AGENT_EVOLVE = "agent_evolve"
260
+ OTHER = "other"
261
+
262
+
263
+ class ExceptionProvenanceLink(str, Enum):
264
+ """Closed relationship from one sanitized exception node to its parent."""
265
+
266
+ ROOT = "root"
267
+ CAUSE = "cause"
268
+ CONTEXT = "context"
269
+ GROUP_MEMBER = "group_member"
270
+
271
+
272
+ @dataclass(frozen=True, slots=True)
273
+ class SanitizedExceptionProvenanceNode:
274
+ """One content-free exception-type identity in a bounded exception graph."""
275
+
276
+ parent_index: Optional[int]
277
+ link: ExceptionProvenanceLink
278
+ family: ExceptionOriginFamily
279
+ type_identity_sha256: str
280
+
281
+ def __post_init__(self) -> None:
282
+ if type(self.link) is not ExceptionProvenanceLink:
283
+ raise TypeError("link must be an ExceptionProvenanceLink")
284
+ if type(self.family) is not ExceptionOriginFamily:
285
+ raise TypeError("family must be an ExceptionOriginFamily")
286
+ if (
287
+ type(self.type_identity_sha256) is not str
288
+ or _SHA256.fullmatch(self.type_identity_sha256) is None
289
+ ):
290
+ raise ValueError("type_identity_sha256 must be lowercase SHA-256")
291
+ if self.link is ExceptionProvenanceLink.ROOT:
292
+ if self.parent_index is not None:
293
+ raise ValueError("root exception provenance cannot have a parent")
294
+ elif type(self.parent_index) is not int or self.parent_index < 0:
295
+ raise ValueError(
296
+ "non-root exception provenance requires a non-negative parent index"
297
+ )
298
+
299
+
300
+ @dataclass(frozen=True, slots=True)
301
+ class SanitizedExceptionProvenance:
302
+ """Bounded, topology-preserving provenance with no exception text.
303
+
304
+ Nodes contain only closed families, SHA-256 fingerprints of bounded type
305
+ identity projections, and integer graph edges. Messages, reprs, response
306
+ objects, URLs, payloads, tracebacks, and arbitrary metadata are absent by
307
+ construction.
308
+ """
309
+
310
+ nodes: Tuple[SanitizedExceptionProvenanceNode, ...]
311
+ truncated: bool
312
+
313
+ def __post_init__(self) -> None:
314
+ if type(self.nodes) is not tuple or not self.nodes:
315
+ raise ValueError("exception provenance nodes must be a non-empty tuple")
316
+ if len(self.nodes) > MAX_EXCEPTION_PROVENANCE_NODES:
317
+ raise ValueError(
318
+ "exception provenance exceeds the bounded node allowance"
319
+ )
320
+ if any(
321
+ type(node) is not SanitizedExceptionProvenanceNode
322
+ for node in self.nodes
323
+ ):
324
+ raise TypeError(
325
+ "exception provenance must contain exact sanitized nodes"
326
+ )
327
+ if type(self.truncated) is not bool:
328
+ raise TypeError("exception provenance truncated must be bool")
329
+ for index, node in enumerate(self.nodes):
330
+ SanitizedExceptionProvenanceNode.__post_init__(node)
331
+ if index == 0:
332
+ if node.link is not ExceptionProvenanceLink.ROOT:
333
+ raise ValueError("first exception provenance node must be root")
334
+ elif (
335
+ node.link is ExceptionProvenanceLink.ROOT
336
+ or node.parent_index is None
337
+ or node.parent_index >= index
338
+ ):
339
+ raise ValueError(
340
+ "exception provenance parents must precede their child nodes"
341
+ )
342
+
343
+
344
+ class ValidationIssueCategory(str, Enum):
345
+ """Coarse validation categories safe to expose to a repair attempt."""
346
+
347
+ MISSING = "missing"
348
+ EXTRA_FIELD = "extra_field"
349
+ LITERAL_OR_ENUM = "literal_or_enum"
350
+ WRONG_TYPE = "wrong_type"
351
+ BOUNDS_OR_LENGTH = "bounds_or_length"
352
+ SEMANTIC_CONSTRAINT = "semantic_constraint"
353
+ MALFORMED_ARGUMENTS = "malformed_arguments"
354
+ OTHER_VALIDATION = "other_validation"
355
+
356
+
357
+ class ValidationIssueReasonCode(str, Enum):
358
+ """Closed semantic-output reasons safe for durable diagnostics.
359
+
360
+ Integrations may populate this field only from a trusted validator error
361
+ *type*. Free-form validation messages, model output, and Pydantic context
362
+ values are never parsed into these codes.
363
+ """
364
+
365
+ DUPLICATE_FINITE_OPTIONS = "duplicate_finite_options"
366
+ FINITE_OPTION_OUT_OF_CONTRACT = "finite_option_out_of_contract"
367
+ ASSIGNED_MEMORY_CARD_OMITTED = "assigned_memory_card_omitted"
368
+ PROPOSAL_SUPPORT_OPTION_OMITTED = "proposal_support_option_omitted"
369
+ NO_FEASIBLE_DISJOINT_PORTFOLIO = "no_feasible_disjoint_portfolio"
370
+ PORTFOLIO_MEMORY_DOSE_VIOLATION = "portfolio_memory_dose_violation"
371
+ REFLECTION_METRIC_CONTRACT_VIOLATION = (
372
+ "reflection_metric_contract_violation"
373
+ )
374
+ REFLECTION_ACTION_CONTRACT_VIOLATION = (
375
+ "reflection_action_contract_violation"
376
+ )
377
+ REFLECTION_SEMANTIC_CONTRACT_VIOLATION = (
378
+ "reflection_semantic_contract_violation"
379
+ )
380
+ REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION = (
381
+ "reflection_direction_or_anchor_violation"
382
+ )
383
+ RESIDUAL_RADIUS_CONTRACT_VIOLATION = (
384
+ "residual_radius_contract_violation"
385
+ )
386
+ RESIDUAL_OPTION_CONTRACT_VIOLATION = (
387
+ "residual_option_contract_violation"
388
+ )
389
+ RESIDUAL_METRIC_CONTRACT_VIOLATION = (
390
+ "residual_metric_contract_violation"
391
+ )
392
+ RESIDUAL_QUANTILE_ORDER_VIOLATION = (
393
+ "residual_quantile_order_violation"
394
+ )
395
+ RESIDUAL_PLAN_DIVERSITY_VIOLATION = (
396
+ "residual_plan_diversity_violation"
397
+ )
398
+
399
+
400
+ class AttemptRequestVariant(str, Enum):
401
+ """Closed variants for the exact prompt used by one provider attempt."""
402
+
403
+ ORIGINAL = "original"
404
+ # Retained so historical telemetry remains decodable. New attempts use v4.
405
+ SCHEMA_REPAIR_V1 = "schema_repair_v1"
406
+ SCHEMA_REPAIR_V2 = "schema_repair_v2"
407
+ SCHEMA_REPAIR_V3 = "schema_repair_v3"
408
+ SCHEMA_REPAIR_V4 = "schema_repair_v4"
409
+
410
+
411
+ @dataclass(frozen=True, slots=True)
412
+ class AttemptRequestEvidence:
413
+ """Content-free identity evidence for the request sent by one attempt."""
414
+
415
+ variant: AttemptRequestVariant
416
+ prompt_sha256: str
417
+ provider_attempt_id: Optional[ProviderAttemptId] = None
418
+
419
+ def __post_init__(self) -> None:
420
+ if type(self.variant) is not AttemptRequestVariant:
421
+ raise TypeError("variant must be an AttemptRequestVariant")
422
+ if (
423
+ type(self.prompt_sha256) is not str
424
+ or _SHA256.fullmatch(self.prompt_sha256) is None
425
+ ):
426
+ raise ValueError("prompt_sha256 must be a lowercase SHA-256 digest")
427
+ if self.provider_attempt_id is not None and (
428
+ type(self.provider_attempt_id) is not ProviderAttemptId
429
+ ):
430
+ raise TypeError("provider_attempt_id must be a ProviderAttemptId or None")
431
+
432
+
433
+ @dataclass(frozen=True, slots=True)
434
+ class SanitizedValidationIssue:
435
+ """One bounded issue with no message, input value, or provider content."""
436
+
437
+ category: ValidationIssueCategory
438
+ location: Tuple[str, ...]
439
+ reason_code: Optional[ValidationIssueReasonCode] = None
440
+
441
+ def __post_init__(self) -> None:
442
+ if type(self.category) is not ValidationIssueCategory:
443
+ raise TypeError("category must be a ValidationIssueCategory")
444
+ if type(self.location) is not tuple:
445
+ raise TypeError("location must be an exact tuple")
446
+ if not 1 <= len(self.location) <= MAX_VALIDATION_LOCATION_DEPTH:
447
+ raise ValueError(
448
+ "location must contain between one and "
449
+ f"{MAX_VALIDATION_LOCATION_DEPTH} safe segments"
450
+ )
451
+ for segment in self.location:
452
+ if (
453
+ type(segment) is not str
454
+ or _VALIDATION_LOCATION_TOKEN.fullmatch(segment) is None
455
+ ):
456
+ raise ValueError("location segments must use the safe token grammar")
457
+ if self.reason_code is not None and (
458
+ type(self.reason_code) is not ValidationIssueReasonCode
459
+ ):
460
+ raise TypeError("reason_code must be a ValidationIssueReasonCode or None")
461
+ if (
462
+ self.reason_code is not None
463
+ and self.category is not ValidationIssueCategory.SEMANTIC_CONSTRAINT
464
+ ):
465
+ raise ValueError(
466
+ "reason_code is valid only for a semantic_constraint issue"
467
+ )
468
+
469
+
470
+ @dataclass(frozen=True, slots=True)
471
+ class RetryAfter:
472
+ """A validated Retry-After delay; sleeping remains the queue's job."""
473
+
474
+ delay_ns: int
475
+ source: RetryAfterSource
476
+
477
+ def __post_init__(self) -> None:
478
+ _require_nonnegative_ns(self.delay_ns, "delay_ns")
479
+ if type(self.source) is not RetryAfterSource:
480
+ raise TypeError("source must be a RetryAfterSource")
481
+
482
+
483
+ def parse_retry_after(value: object, *, now_utc: datetime) -> Optional[RetryAfter]:
484
+ """Parse an HTTP Retry-After value without sleeping or provider coupling.
485
+
486
+ RFC delay-seconds and timezone-aware HTTP dates are accepted. Invalid,
487
+ non-ASCII, fractional, or unbounded values return ``None``. Past dates map
488
+ to a zero delay. Date resolution is rounded up to the next nanosecond so a
489
+ parsed server delay is never shortened.
490
+ """
491
+
492
+ if type(value) is not str or not value or len(value) > 128 or not value.isascii():
493
+ return None
494
+ text = value.strip()
495
+ if _RETRY_AFTER_SECONDS.fullmatch(text):
496
+ seconds = int(text)
497
+ if seconds > (2**63 - 1) // NANOSECONDS_PER_SECOND:
498
+ return None
499
+ return RetryAfter(
500
+ delay_ns=seconds * NANOSECONDS_PER_SECOND,
501
+ source=RetryAfterSource.DELAY_SECONDS,
502
+ )
503
+
504
+ if type(now_utc) is not datetime or now_utc.utcoffset() is None:
505
+ raise ValueError("now_utc must be a timezone-aware datetime")
506
+ try:
507
+ parsed = parsedate_to_datetime(text)
508
+ except (TypeError, ValueError, OverflowError):
509
+ return None
510
+ if parsed is None or parsed.utcoffset() is None:
511
+ return None
512
+ delta_seconds = (
513
+ parsed.astimezone(timezone.utc) - now_utc.astimezone(timezone.utc)
514
+ ).total_seconds()
515
+ if not math.isfinite(delta_seconds):
516
+ return None
517
+ delay_ns = max(0, math.ceil(delta_seconds * NANOSECONDS_PER_SECOND))
518
+ if delay_ns > 2**63 - 1:
519
+ return None
520
+ return RetryAfter(delay_ns=delay_ns, source=RetryAfterSource.HTTP_DATE)
521
+
522
+
523
+ @dataclass(frozen=True, slots=True)
524
+ class SanitizedAttemptFailure:
525
+ """Bounded failure evidence safe to retain in durable attempt telemetry.
526
+
527
+ The queue never derives this value from ``str(error)``. Integrations may
528
+ attach it only after replacing provider text and response bodies with a
529
+ closed kind and an explicitly sanitized message.
530
+ """
531
+
532
+ kind: str
533
+ retryable: bool
534
+ safe_message: str
535
+ status_code: Optional[int] = None
536
+ retry_after_seconds: Optional[float] = None
537
+ output_failure_mode: Optional[StructuredOutputFailureMode] = None
538
+ validation_issues: Tuple[SanitizedValidationIssue, ...] = ()
539
+ stream_timeout_phase: Optional[StreamTimeoutPhase] = None
540
+ provider_error_code: Optional[CanonicalProviderErrorCode] = None
541
+ provider_error_envelope_sha256: Optional[str] = None
542
+ exception_provenance: Optional[SanitizedExceptionProvenance] = None
543
+
544
+ def __post_init__(self) -> None:
545
+ if type(self.kind) is not str or _FAILURE_KIND.fullmatch(self.kind) is None:
546
+ raise ValueError("kind must use the closed lowercase token grammar")
547
+ if type(self.retryable) is not bool:
548
+ raise TypeError("retryable must be bool")
549
+ if type(self.safe_message) is not str or not self.safe_message.strip():
550
+ raise ValueError("safe_message must be non-empty")
551
+ if len(self.safe_message.encode("utf-8", errors="strict")) > 512:
552
+ raise ValueError("safe_message is too large for inline telemetry")
553
+ if self.status_code is not None and (
554
+ type(self.status_code) is not int or not 100 <= self.status_code <= 599
555
+ ):
556
+ raise ValueError("status_code must be an HTTP status or None")
557
+ if self.retry_after_seconds is not None and (
558
+ isinstance(self.retry_after_seconds, bool)
559
+ or not isinstance(self.retry_after_seconds, (int, float))
560
+ or not math.isfinite(float(self.retry_after_seconds))
561
+ or float(self.retry_after_seconds) < 0
562
+ ):
563
+ raise ValueError("retry_after_seconds must be finite and non-negative")
564
+ if (
565
+ self.output_failure_mode is not None
566
+ and type(self.output_failure_mode) is not StructuredOutputFailureMode
567
+ ):
568
+ raise TypeError(
569
+ "output_failure_mode must be a StructuredOutputFailureMode or None"
570
+ )
571
+ if self.output_failure_mode is not None and self.kind != "output_invalid":
572
+ raise ValueError(
573
+ "only output_invalid failures may carry output diagnostics"
574
+ )
575
+ if type(self.validation_issues) is not tuple:
576
+ raise TypeError("validation_issues must be an exact tuple")
577
+ if len(self.validation_issues) > MAX_VALIDATION_ISSUES:
578
+ raise ValueError(
579
+ f"validation_issues cannot exceed {MAX_VALIDATION_ISSUES} entries"
580
+ )
581
+ if any(
582
+ type(issue) is not SanitizedValidationIssue
583
+ for issue in self.validation_issues
584
+ ):
585
+ raise TypeError(
586
+ "validation_issues must contain exact SanitizedValidationIssue values"
587
+ )
588
+ if self.output_failure_mode is None and self.validation_issues:
589
+ raise ValueError("validation issues require an output failure mode")
590
+ if (
591
+ self.validation_issues
592
+ and self.output_failure_mode
593
+ is not StructuredOutputFailureMode.SCHEMA_VALIDATION
594
+ ):
595
+ raise ValueError("validation issues require schema_validation mode")
596
+ if self.stream_timeout_phase is not None and (
597
+ type(self.stream_timeout_phase) is not StreamTimeoutPhase
598
+ ):
599
+ raise TypeError("stream_timeout_phase must be a StreamTimeoutPhase or None")
600
+ if self.stream_timeout_phase is not None and self.kind != "timeout":
601
+ raise ValueError("only timeout failures may carry a stream timeout phase")
602
+ if self.provider_error_code is not None and (
603
+ type(self.provider_error_code) is not CanonicalProviderErrorCode
604
+ ):
605
+ raise TypeError(
606
+ "provider_error_code must be a CanonicalProviderErrorCode or None"
607
+ )
608
+ if self.provider_error_envelope_sha256 is not None and (
609
+ type(self.provider_error_envelope_sha256) is not str
610
+ or _SHA256.fullmatch(self.provider_error_envelope_sha256) is None
611
+ ):
612
+ raise ValueError(
613
+ "provider_error_envelope_sha256 must be lowercase SHA-256 or None"
614
+ )
615
+ if (
616
+ self.provider_error_code is not None
617
+ or self.provider_error_envelope_sha256 is not None
618
+ ) and self.status_code is None:
619
+ raise ValueError("provider HTTP diagnostics require status_code")
620
+ if self.exception_provenance is not None and (
621
+ type(self.exception_provenance) is not SanitizedExceptionProvenance
622
+ ):
623
+ raise TypeError(
624
+ "exception_provenance must be SanitizedExceptionProvenance or None"
625
+ )
626
+ if self.exception_provenance is not None:
627
+ SanitizedExceptionProvenance.__post_init__(self.exception_provenance)
628
+ if self.kind != "unknown":
629
+ raise ValueError(
630
+ "exception provenance is retained only for unknown failures"
631
+ )
632
+
633
+
634
+ @dataclass(frozen=True, slots=True)
635
+ class RetryClassification:
636
+ disposition: RetryDisposition
637
+ reason: RetryReason
638
+ retry_after: Optional[RetryAfter] = None
639
+ sanitized_failure: Optional[SanitizedAttemptFailure] = None
640
+
641
+ def __post_init__(self) -> None:
642
+ if type(self.disposition) is not RetryDisposition:
643
+ raise TypeError("disposition must be a RetryDisposition")
644
+ if type(self.reason) is not RetryReason:
645
+ raise TypeError("reason must be a RetryReason")
646
+ if self.retry_after is not None and type(self.retry_after) is not RetryAfter:
647
+ raise TypeError("retry_after must be a RetryAfter or None")
648
+ if (
649
+ self.sanitized_failure is not None
650
+ and type(self.sanitized_failure) is not SanitizedAttemptFailure
651
+ ):
652
+ raise TypeError(
653
+ "sanitized_failure must be a SanitizedAttemptFailure or None"
654
+ )
655
+ if self.disposition is RetryDisposition.FAIL and self.retry_after is not None:
656
+ raise ValueError("a terminal classification cannot carry Retry-After")
657
+
658
+
659
+ @dataclass(frozen=True, slots=True)
660
+ class LLMTask(Generic[RequestT]):
661
+ task_id: str
662
+ request: RequestT
663
+ max_attempts: int
664
+ attempt_timeout_ns: Optional[int] = None
665
+ retry_budget: Optional[PartitionedRetryBudget] = None
666
+
667
+ def __post_init__(self) -> None:
668
+ if type(self.task_id) is not str or _TASK_ID.fullmatch(self.task_id) is None:
669
+ raise ValueError(
670
+ "task_id must be a non-secret ASCII identifier of at most "
671
+ f"{MAX_TASK_ID_LENGTH} characters"
672
+ )
673
+ if (
674
+ type(self.max_attempts) is not int
675
+ or not 1 <= self.max_attempts <= MAX_ATTEMPTS
676
+ ):
677
+ raise ValueError(f"max_attempts must be an integer in [1, {MAX_ATTEMPTS}]")
678
+ if self.attempt_timeout_ns is not None:
679
+ _require_positive_ns(self.attempt_timeout_ns, "attempt_timeout_ns")
680
+ if self.retry_budget is not None and (
681
+ type(self.retry_budget) is not PartitionedRetryBudget
682
+ ):
683
+ raise TypeError(
684
+ "retry_budget must be a PartitionedRetryBudget or None"
685
+ )
686
+ if self.retry_budget is not None:
687
+ PartitionedRetryBudget.__post_init__(self.retry_budget)
688
+
689
+
690
+ @dataclass(frozen=True, slots=True)
691
+ class LLMAttemptContext:
692
+ task_id: str
693
+ attempt_number: int
694
+ attempt_timeout_ns: Optional[int]
695
+ previous_failure: Optional[SanitizedAttemptFailure] = None
696
+ active_output_failure: Optional[SanitizedAttemptFailure] = None
697
+ retry_budget_usage: Optional[RetryBudgetUsage] = None
698
+
699
+ def __post_init__(self) -> None:
700
+ if type(self.task_id) is not str or _TASK_ID.fullmatch(self.task_id) is None:
701
+ raise ValueError("task_id violates the queue identifier policy")
702
+ if type(self.attempt_number) is not int or self.attempt_number < 1:
703
+ raise ValueError("attempt_number must be a positive integer")
704
+ if self.attempt_timeout_ns is not None:
705
+ _require_positive_ns(self.attempt_timeout_ns, "attempt_timeout_ns")
706
+ if (
707
+ self.previous_failure is not None
708
+ and type(self.previous_failure) is not SanitizedAttemptFailure
709
+ ):
710
+ raise TypeError(
711
+ "previous_failure must be a SanitizedAttemptFailure or None"
712
+ )
713
+ if (
714
+ self.active_output_failure is not None
715
+ and type(self.active_output_failure) is not SanitizedAttemptFailure
716
+ ):
717
+ raise TypeError(
718
+ "active_output_failure must be a SanitizedAttemptFailure or None"
719
+ )
720
+ if self.active_output_failure is not None and (
721
+ self.active_output_failure.kind != "output_invalid"
722
+ or not self.active_output_failure.retryable
723
+ ):
724
+ raise ValueError(
725
+ "active_output_failure must be a retryable output_invalid failure"
726
+ )
727
+ if self.retry_budget_usage is not None and (
728
+ type(self.retry_budget_usage) is not RetryBudgetUsage
729
+ ):
730
+ raise TypeError(
731
+ "retry_budget_usage must be a RetryBudgetUsage or None"
732
+ )
733
+ if self.retry_budget_usage is not None:
734
+ RetryBudgetUsage.__post_init__(self.retry_budget_usage)
735
+ admitted_retries = (
736
+ self.retry_budget_usage.output_invalid_retries
737
+ + self.retry_budget_usage.transport_retries
738
+ )
739
+ if admitted_retries != self.attempt_number - 1:
740
+ raise ValueError(
741
+ "partitioned retry usage must account for every prior attempt"
742
+ )
743
+ if self.attempt_number == 1 and (
744
+ self.previous_failure is not None or self.active_output_failure is not None
745
+ ):
746
+ raise ValueError("attempt one cannot carry prior failure state")
747
+
748
+
749
+ class AttemptStatus(str, Enum):
750
+ SUCCEEDED = "succeeded"
751
+ RETRYABLE_FAILURE = "retryable_failure"
752
+ TERMINAL_FAILURE = "terminal_failure"
753
+ TIMED_OUT = "timed_out"
754
+ CANCELLED = "cancelled"
755
+
756
+
757
+ @dataclass(frozen=True, slots=True)
758
+ class AttemptTelemetry:
759
+ """One provider attempt's wait, execution, and retry scheduling evidence.
760
+
761
+ ``wait_time_ns`` is initial scheduler queue time for attempt one and the
762
+ actual inter-attempt wait for later attempts. ``service_time_ns`` covers
763
+ only execution inside the timeout owner.
764
+ """
765
+
766
+ attempt_number: int
767
+ status: AttemptStatus
768
+ wait_time_ns: int
769
+ service_time_ns: int
770
+ will_retry: bool
771
+ policy_backoff_ns: int = 0
772
+ retry_after_ns: int = 0
773
+ scheduled_delay_ns: int = 0
774
+ classification: Optional[RetryClassification] = None
775
+ error_type: Optional[str] = None
776
+ request_evidence: Optional[AttemptRequestEvidence] = None
777
+
778
+ def __post_init__(self) -> None:
779
+ if type(self.attempt_number) is not int or self.attempt_number < 1:
780
+ raise ValueError("attempt_number must be a positive integer")
781
+ if type(self.status) is not AttemptStatus:
782
+ raise TypeError("status must be an AttemptStatus")
783
+ for name, value in (
784
+ ("wait_time_ns", self.wait_time_ns),
785
+ ("service_time_ns", self.service_time_ns),
786
+ ("policy_backoff_ns", self.policy_backoff_ns),
787
+ ("retry_after_ns", self.retry_after_ns),
788
+ ("scheduled_delay_ns", self.scheduled_delay_ns),
789
+ ):
790
+ _require_nonnegative_ns(value, name)
791
+ if type(self.will_retry) is not bool:
792
+ raise TypeError("will_retry must be bool")
793
+ if (
794
+ self.classification is not None
795
+ and type(self.classification) is not RetryClassification
796
+ ):
797
+ raise TypeError("classification must be a RetryClassification or None")
798
+ if self.error_type is not None and (
799
+ type(self.error_type) is not str
800
+ or not self.error_type
801
+ or len(self.error_type) > 256
802
+ ):
803
+ raise ValueError("error_type must be a bounded non-empty string or None")
804
+ if (
805
+ self.request_evidence is not None
806
+ and type(self.request_evidence) is not AttemptRequestEvidence
807
+ ):
808
+ raise TypeError(
809
+ "request_evidence must be an AttemptRequestEvidence or None"
810
+ )
811
+
812
+ if self.status is AttemptStatus.SUCCEEDED:
813
+ if (
814
+ self.will_retry
815
+ or self.classification is not None
816
+ or self.error_type is not None
817
+ or self.policy_backoff_ns
818
+ or self.retry_after_ns
819
+ or self.scheduled_delay_ns
820
+ ):
821
+ raise ValueError("successful attempt telemetry carries failure fields")
822
+ return
823
+
824
+ if self.status is AttemptStatus.CANCELLED:
825
+ if self.will_retry or self.classification is not None:
826
+ raise ValueError("cancelled attempt cannot schedule a retry")
827
+ return
828
+
829
+ if self.classification is None or self.error_type is None:
830
+ raise ValueError(
831
+ "failed attempt telemetry requires classification and error type"
832
+ )
833
+ expected_retry_after = (
834
+ self.classification.retry_after.delay_ns
835
+ if self.classification.retry_after is not None
836
+ else 0
837
+ )
838
+ if self.retry_after_ns != expected_retry_after:
839
+ raise ValueError("retry_after_ns disagrees with the classification")
840
+ if self.will_retry:
841
+ if self.classification.disposition is not RetryDisposition.RETRY:
842
+ raise ValueError("will_retry requires a retry classification")
843
+ if self.scheduled_delay_ns != max(
844
+ self.policy_backoff_ns,
845
+ self.retry_after_ns,
846
+ ):
847
+ raise ValueError(
848
+ "scheduled delay must honor policy backoff and Retry-After"
849
+ )
850
+ elif self.policy_backoff_ns or self.scheduled_delay_ns:
851
+ raise ValueError("a terminal attempt cannot carry a scheduled backoff")
852
+
853
+
854
+ class TaskOutcomeStatus(str, Enum):
855
+ SUCCEEDED = "succeeded"
856
+ TERMINAL_FAILURE = "terminal_failure"
857
+ ATTEMPTS_EXHAUSTED = "attempts_exhausted"
858
+ CANCELLED = "cancelled"
859
+
860
+
861
+ class CancellationReason(str, Enum):
862
+ QUEUE_CLOSED = "queue_closed"
863
+ EXECUTOR_RETIRED = "executor_retired"
864
+ SUBMITTER_CANCELLED = "submitter_cancelled"
865
+
866
+
867
+ @dataclass(frozen=True, slots=True)
868
+ class TaskTelemetry:
869
+ """Whole-task timing.
870
+
871
+ Queue time ends at the first attempt start. Service time runs from that
872
+ start through the terminal outcome and therefore includes retry waits.
873
+ """
874
+
875
+ task_id: str
876
+ queue_time_ns: int
877
+ service_time_ns: int
878
+ total_time_ns: int
879
+ attempts: Tuple[AttemptTelemetry, ...]
880
+
881
+ def __post_init__(self) -> None:
882
+ if type(self.task_id) is not str or _TASK_ID.fullmatch(self.task_id) is None:
883
+ raise ValueError("task_id violates the queue identifier policy")
884
+ for name, value in (
885
+ ("queue_time_ns", self.queue_time_ns),
886
+ ("service_time_ns", self.service_time_ns),
887
+ ("total_time_ns", self.total_time_ns),
888
+ ):
889
+ _require_nonnegative_ns(value, name)
890
+ if type(self.attempts) is not tuple or any(
891
+ type(attempt) is not AttemptTelemetry for attempt in self.attempts
892
+ ):
893
+ raise TypeError(
894
+ "attempts must be an exact tuple of AttemptTelemetry values"
895
+ )
896
+ if [attempt.attempt_number for attempt in self.attempts] != list(
897
+ range(1, len(self.attempts) + 1)
898
+ ):
899
+ raise ValueError("attempt telemetry numbers must be contiguous")
900
+ if self.queue_time_ns + self.service_time_ns != self.total_time_ns:
901
+ raise ValueError("total_time_ns must equal queue plus service time")
902
+
903
+
904
+ @dataclass(frozen=True, slots=True)
905
+ class LLMTaskOutcome(Generic[ResponseT]):
906
+ status: TaskOutcomeStatus
907
+ telemetry: TaskTelemetry
908
+ response: Optional[ResponseT] = None
909
+ cancellation_reason: Optional[CancellationReason] = None
910
+
911
+ def __post_init__(self) -> None:
912
+ if type(self.status) is not TaskOutcomeStatus:
913
+ raise TypeError("status must be a TaskOutcomeStatus")
914
+ if type(self.telemetry) is not TaskTelemetry:
915
+ raise TypeError("telemetry must be a TaskTelemetry")
916
+ if self.status is TaskOutcomeStatus.SUCCEEDED:
917
+ if self.cancellation_reason is not None:
918
+ raise ValueError("a successful task cannot carry cancellation state")
919
+ if not self.telemetry.attempts or (
920
+ self.telemetry.attempts[-1].status is not AttemptStatus.SUCCEEDED
921
+ ):
922
+ raise ValueError(
923
+ "a successful task requires a successful final attempt"
924
+ )
925
+ elif self.status is TaskOutcomeStatus.CANCELLED:
926
+ if type(self.cancellation_reason) is not CancellationReason:
927
+ raise ValueError("a cancelled task requires a cancellation reason")
928
+ if self.response is not None:
929
+ raise ValueError("a cancelled task cannot carry a response")
930
+ else:
931
+ if self.response is not None or self.cancellation_reason is not None:
932
+ raise ValueError(
933
+ "a failed task cannot carry response or cancellation state"
934
+ )
935
+ if not self.telemetry.attempts:
936
+ raise ValueError("a failed task requires at least one attempt")
937
+
938
+
939
+ @dataclass(frozen=True, slots=True)
940
+ class QueueSnapshot:
941
+ max_in_flight: int
942
+ max_pending: int
943
+ in_flight: int
944
+ pending: int
945
+ closed: bool
946
+
947
+ def __post_init__(self) -> None:
948
+ for name, value in (
949
+ ("max_in_flight", self.max_in_flight),
950
+ ("max_pending", self.max_pending),
951
+ ("in_flight", self.in_flight),
952
+ ("pending", self.pending),
953
+ ):
954
+ _require_nonnegative_int(value, name)
955
+ if self.max_in_flight < 1:
956
+ raise ValueError("max_in_flight must be positive")
957
+ if self.in_flight > self.max_in_flight or self.pending > self.max_pending:
958
+ raise ValueError("queue snapshot exceeds configured bounds")
959
+ if type(self.closed) is not bool:
960
+ raise TypeError("closed must be bool")