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.
- agent_evolve/__init__.py +722 -0
- agent_evolve/agentic.py +2800 -0
- agent_evolve/api.py +767 -0
- agent_evolve/application/__init__.py +1580 -0
- agent_evolve/application/action_allocation.py +744 -0
- agent_evolve/application/action_allocation_frame.py +347 -0
- agent_evolve/application/action_allocation_frame_commit.py +185 -0
- agent_evolve/application/action_allocation_frame_commit_v3.py +184 -0
- agent_evolve/application/action_allocation_frame_v3.py +338 -0
- agent_evolve/application/action_archive_value.py +497 -0
- agent_evolve/application/action_evidence_consistency.py +455 -0
- agent_evolve/application/action_forecast_partitioning.py +1471 -0
- agent_evolve/application/action_metric_projection.py +211 -0
- agent_evolve/application/action_role_value.py +680 -0
- agent_evolve/application/action_score_authorities.py +363 -0
- agent_evolve/application/action_structural_signature.py +116 -0
- agent_evolve/application/action_target_realization.py +402 -0
- agent_evolve/application/agentic_evolution.py +7734 -0
- agent_evolve/application/agentic_portfolio_residual_expert.py +835 -0
- agent_evolve/application/anchor_residual_identification.py +463 -0
- agent_evolve/application/archive_conditioned_action_target.py +208 -0
- agent_evolve/application/artifact_journal.py +246 -0
- agent_evolve/application/artifact_replay.py +347 -0
- agent_evolve/application/budgeted_optimizer.py +1828 -0
- agent_evolve/application/calibrated_campaign.py +485 -0
- agent_evolve/application/calibrated_current_prefix_forecast_opportunity.py +322 -0
- agent_evolve/application/calibrated_positive_gain_opportunity.py +1581 -0
- agent_evolve/application/campaign_capacity_recourse.py +254 -0
- agent_evolve/application/campaign_contextual_outcomes.py +119 -0
- agent_evolve/application/campaign_diagnostic_blocks.py +930 -0
- agent_evolve/application/campaign_evidence_registry.py +262 -0
- agent_evolve/application/campaign_execution.py +2537 -0
- agent_evolve/application/campaign_generation_audit.py +942 -0
- agent_evolve/application/campaign_learning.py +1812 -0
- agent_evolve/application/campaign_learning_runtime.py +1977 -0
- agent_evolve/application/campaign_search_phase.py +227 -0
- agent_evolve/application/campaign_selector_context_extension.py +220 -0
- agent_evolve/application/campaign_variation_envelope.py +649 -0
- agent_evolve/application/campaign_variation_trace.py +451 -0
- agent_evolve/application/candidate_archive_consequence.py +128 -0
- agent_evolve/application/causal_opportunity_portfolio_gate.py +385 -0
- agent_evolve/application/composite_outcome_updater.py +145 -0
- agent_evolve/application/composition_portfolio_selection.py +363 -0
- agent_evolve/application/concurrent_stage.py +144 -0
- agent_evolve/application/contextual_action_allocation.py +181 -0
- agent_evolve/application/contextual_campaign_outcomes.py +267 -0
- agent_evolve/application/contextual_campaign_planning.py +1366 -0
- agent_evolve/application/contextual_delayed_credit.py +651 -0
- agent_evolve/application/contextual_search_controller.py +2374 -0
- agent_evolve/application/current_prefix_forecast_opportunity.py +714 -0
- agent_evolve/application/decision_metric_projection.py +112 -0
- agent_evolve/application/derived_action_semantics.py +129 -0
- agent_evolve/application/detailed_evaluation.py +449 -0
- agent_evolve/application/earned_lineage.py +1011 -0
- agent_evolve/application/effective_choice_audit.py +484 -0
- agent_evolve/application/empirical_consequence_calibration.py +908 -0
- agent_evolve/application/evaluation_accounting.py +325 -0
- agent_evolve/application/evaluation_cache.py +199 -0
- agent_evolve/application/evaluation_escrow.py +547 -0
- agent_evolve/application/evaluation_recourse.py +253 -0
- agent_evolve/application/event_recorder.py +151 -0
- agent_evolve/application/evolution_campaign.py +1840 -0
- agent_evolve/application/executable_hypothesis.py +323 -0
- agent_evolve/application/factorial_branch_pilot.py +772 -0
- agent_evolve/application/finite_acquisition_capacity_recourse.py +672 -0
- agent_evolve/application/finite_acquisition_residual_expert.py +373 -0
- agent_evolve/application/finite_acquisition_variation_envelope.py +802 -0
- agent_evolve/application/finite_action_hypothesis_semantics.py +446 -0
- agent_evolve/application/finite_action_selection.py +188 -0
- agent_evolve/application/finite_action_set.py +306 -0
- agent_evolve/application/finite_action_transition.py +537 -0
- agent_evolve/application/finite_variation_eligibility.py +296 -0
- agent_evolve/application/forecast_geometry_portfolio.py +799 -0
- agent_evolve/application/forecast_opportunity_shadow_calibration.py +316 -0
- agent_evolve/application/front_proximity_admission.py +311 -0
- agent_evolve/application/front_proximity_parent_basis.py +458 -0
- agent_evolve/application/frozen_hurdle_score.py +659 -0
- agent_evolve/application/g3_causal_screen.py +2257 -0
- agent_evolve/application/g3_causal_validation.py +1046 -0
- agent_evolve/application/g3_postseal_curation.py +818 -0
- agent_evolve/application/gated_agentic_generator.py +205 -0
- agent_evolve/application/generation_feedback.py +293 -0
- agent_evolve/application/generative_proposal_journal.py +185 -0
- agent_evolve/application/geometry_conditional_elasticity.py +453 -0
- agent_evolve/application/global_wave_action_allocation.py +1151 -0
- agent_evolve/application/head_mass_conditional_seat.py +268 -0
- agent_evolve/application/identifiable_reflection_evidence.py +1147 -0
- agent_evolve/application/identifiable_reflection_learning.py +395 -0
- agent_evolve/application/identifiable_reflection_request.py +364 -0
- agent_evolve/application/in_memory_residual_archive.py +341 -0
- agent_evolve/application/insight_memory.py +1804 -0
- agent_evolve/application/live_runtime_manifest.py +758 -0
- agent_evolve/application/llm_task_queue.py +769 -0
- agent_evolve/application/matched_finite_action_block.py +409 -0
- agent_evolve/application/materialized_action_broker.py +2328 -0
- agent_evolve/application/materialized_action_constraints.py +83 -0
- agent_evolve/application/materialized_variation.py +211 -0
- agent_evolve/application/multi_option_evolution.py +1536 -0
- agent_evolve/application/outcome_adaptive_action_racing.py +2827 -0
- agent_evolve/application/outcome_adaptive_residual_campaign_runtime.py +580 -0
- agent_evolve/application/outcome_adaptive_residual_portfolio_evolution.py +3671 -0
- agent_evolve/application/outcome_conditioned_portfolio_selection.py +1374 -0
- agent_evolve/application/outcome_relation.py +193 -0
- agent_evolve/application/paired_allocation_comparison.py +241 -0
- agent_evolve/application/paired_block_schedule.py +127 -0
- agent_evolve/application/parent_measurement.py +226 -0
- agent_evolve/application/pareto_archive.py +811 -0
- agent_evolve/application/portfolio_campaign_runtime.py +4739 -0
- agent_evolve/application/portfolio_evolution.py +2950 -0
- agent_evolve/application/portfolio_hypothesis_observations.py +814 -0
- agent_evolve/application/portfolio_memory_attribution.py +581 -0
- agent_evolve/application/portfolio_memory_dose.py +788 -0
- agent_evolve/application/portfolio_memory_matched_control.py +938 -0
- agent_evolve/application/portfolio_memory_transfer.py +297 -0
- agent_evolve/application/portfolio_optimization_memory.py +363 -0
- agent_evolve/application/portfolio_outcome_feedback.py +1613 -0
- agent_evolve/application/portfolio_projection.py +335 -0
- agent_evolve/application/portfolio_recombination.py +2032 -0
- agent_evolve/application/post_evolution_reflection.py +834 -0
- agent_evolve/application/postcommit_rank_authority.py +245 -0
- agent_evolve/application/precommitted_portfolio_racing.py +2762 -0
- agent_evolve/application/prequential_archive_opportunity_calibration.py +1154 -0
- agent_evolve/application/prequential_residual_exploration.py +343 -0
- agent_evolve/application/prequential_score_portfolio.py +954 -0
- agent_evolve/application/projections.py +292 -0
- agent_evolve/application/protected_action_committee.py +1027 -0
- agent_evolve/application/protected_branch_pilot.py +376 -0
- agent_evolve/application/protected_current_prefix_forecast_opportunity.py +552 -0
- agent_evolve/application/provider_replay.py +910 -0
- agent_evolve/application/rank_balanced_causal_pilot.py +1372 -0
- agent_evolve/application/recombination_residual_expert.py +403 -0
- agent_evolve/application/reflection_workflow.py +571 -0
- agent_evolve/application/region_conditional_credit.py +911 -0
- agent_evolve/application/residual_campaign_runtime.py +531 -0
- agent_evolve/application/residual_headroom_campaign_runtime.py +459 -0
- agent_evolve/application/residual_headroom_ledger.py +1544 -0
- agent_evolve/application/residual_learning_transaction.py +396 -0
- agent_evolve/application/residual_portfolio_evolution.py +1228 -0
- agent_evolve/application/residual_reachability.py +749 -0
- agent_evolve/application/residual_stage_credit.py +499 -0
- agent_evolve/application/same_prefix_paired_audit.py +1580 -0
- agent_evolve/application/semantic_coverage_score_portfolio.py +838 -0
- agent_evolve/application/sequential_lineage_allocation.py +1017 -0
- agent_evolve/application/sequential_market_replay.py +1395 -0
- agent_evolve/application/sequential_residual_campaign_runtime.py +305 -0
- agent_evolve/application/sequential_residual_portfolio_evolution.py +940 -0
- agent_evolve/application/single_score_action_allocation.py +299 -0
- agent_evolve/application/source_exposure_allocation.py +906 -0
- agent_evolve/application/staged_memory.py +210 -0
- agent_evolve/application/stratified_cold_start_allocation.py +732 -0
- agent_evolve/application/support_guarded_hurdle_score.py +549 -0
- agent_evolve/application/target_conditioned_action_forecast.py +595 -0
- agent_evolve/application/target_conditioned_campaign.py +566 -0
- agent_evolve/application/treatment_assignment.py +201 -0
- agent_evolve/application/trusted_objective_evidence.py +217 -0
- agent_evolve/application/two_stage_action_evolution.py +1131 -0
- agent_evolve/application/v8lite_allocation_policy.py +1083 -0
- agent_evolve/application/v9_candidate_policy.py +1303 -0
- agent_evolve/bootstrap.py +108 -0
- agent_evolve/campaign_presets.py +517 -0
- agent_evolve/campaign_profiles.py +452 -0
- agent_evolve/campaign_variation_topology.py +288 -0
- agent_evolve/campaign_workload.py +950 -0
- agent_evolve/cli.py +797 -0
- agent_evolve/contract.py +241 -0
- agent_evolve/core/__init__.py +91 -0
- agent_evolve/core/action_semantics.py +411 -0
- agent_evolve/core/authored.py +105 -0
- agent_evolve/core/formatting.py +286 -0
- agent_evolve/core/optimization_semantics.py +324 -0
- agent_evolve/core/problem.py +167 -0
- agent_evolve/core/results.py +323 -0
- agent_evolve/core/stats.py +70 -0
- agent_evolve/core/telemetry.py +100 -0
- agent_evolve/domain/__init__.py +89 -0
- agent_evolve/domain/artifact.py +162 -0
- agent_evolve/domain/durable_text.py +68 -0
- agent_evolve/domain/event.py +1454 -0
- agent_evolve/domain/finite_action_set.py +426 -0
- agent_evolve/domain/finite_variation.py +526 -0
- agent_evolve/domain/generative_emission.py +559 -0
- agent_evolve/domain/ids.py +163 -0
- agent_evolve/domain/inline_text.py +106 -0
- agent_evolve/domain/insight.py +27 -0
- agent_evolve/domain/lineage.py +737 -0
- agent_evolve/domain/llm_task_queue.py +960 -0
- agent_evolve/domain/outcome.py +96 -0
- agent_evolve/domain/patch.py +854 -0
- agent_evolve/domain/typed_json.py +542 -0
- agent_evolve/domain/variation_space.py +158 -0
- agent_evolve/driver.py +1014 -0
- agent_evolve/harness/__init__.py +29 -0
- agent_evolve/harness/base.py +242 -0
- agent_evolve/harness/directives.py +163 -0
- agent_evolve/harness/generative_seal.py +479 -0
- agent_evolve/harness/registry.py +41 -0
- agent_evolve/infrastructure/__init__.py +39 -0
- agent_evolve/infrastructure/artifacts/__init__.py +6 -0
- agent_evolve/infrastructure/artifacts/_verification.py +67 -0
- agent_evolve/infrastructure/artifacts/filesystem.py +343 -0
- agent_evolve/infrastructure/artifacts/in_memory.py +73 -0
- agent_evolve/infrastructure/asyncio_runtime.py +109 -0
- agent_evolve/infrastructure/authored_runtime.py +188 -0
- agent_evolve/infrastructure/authored_worker.py +171 -0
- agent_evolve/infrastructure/clock.py +53 -0
- agent_evolve/infrastructure/events/__init__.py +6 -0
- agent_evolve/infrastructure/events/_validation.py +89 -0
- agent_evolve/infrastructure/events/in_memory.py +56 -0
- agent_evolve/infrastructure/events/jsonl.py +193 -0
- agent_evolve/infrastructure/exception_provenance.py +215 -0
- agent_evolve/infrastructure/ids.py +118 -0
- agent_evolve/infrastructure/lineage_codec.py +1836 -0
- agent_evolve/infrastructure/outcome_adaptive_phase_journal.py +170 -0
- agent_evolve/infrastructure/residual_headroom_journal.py +221 -0
- agent_evolve/infrastructure/resource_lease.py +370 -0
- agent_evolve/infrastructure/sanitization/__init__.py +8 -0
- agent_evolve/infrastructure/sanitization/strict_json.py +484 -0
- agent_evolve/infrastructure/sequential_phase_journal.py +170 -0
- agent_evolve/infrastructure/stream_liveness.py +383 -0
- agent_evolve/infrastructure/subprocess_boundary.py +136 -0
- agent_evolve/integrations/__init__.py +1 -0
- agent_evolve/integrations/botorch/__init__.py +28 -0
- agent_evolve/integrations/botorch/finite_qlognehvi.py +190 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch.py +155 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_identity.py +20 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_worker.py +55 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_identity.py +22 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_worker.py +55 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi.py +261 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi_batch.py +273 -0
- agent_evolve/integrations/completion.py +242 -0
- agent_evolve/integrations/pydantic_ai/__init__.py +441 -0
- agent_evolve/integrations/pydantic_ai/action_forecast.py +1068 -0
- agent_evolve/integrations/pydantic_ai/agentic_generator.py +2308 -0
- agent_evolve/integrations/pydantic_ai/async_generator.py +1604 -0
- agent_evolve/integrations/pydantic_ai/boundary_codec.py +1526 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_campaign.py +756 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_selection.py +7537 -0
- agent_evolve/integrations/pydantic_ai/campaign_acquisition.py +609 -0
- agent_evolve/integrations/pydantic_ai/execution_binding.py +138 -0
- agent_evolve/integrations/pydantic_ai/forecast_geometry_action_committee.py +217 -0
- agent_evolve/integrations/pydantic_ai/harness.py +159 -0
- agent_evolve/integrations/pydantic_ai/heterogeneous_model_execution.py +306 -0
- agent_evolve/integrations/pydantic_ai/hierarchical_residual_adaptive_semantic_view.py +179 -0
- agent_evolve/integrations/pydantic_ai/json_schema_dialect.py +108 -0
- agent_evolve/integrations/pydantic_ai/materialized_hierarchical_residual_expert.py +952 -0
- agent_evolve/integrations/pydantic_ai/materialized_portfolio_judge.py +520 -0
- agent_evolve/integrations/pydantic_ai/model_execution_profile.py +659 -0
- agent_evolve/integrations/pydantic_ai/outbound_request_manifest.py +1170 -0
- agent_evolve/integrations/pydantic_ai/portable_residual_consequence_features.py +575 -0
- agent_evolve/integrations/pydantic_ai/portfolio_selection.py +422 -0
- agent_evolve/integrations/pydantic_ai/progress_aware_openrouter.py +416 -0
- agent_evolve/integrations/pydantic_ai/provider_attempt_join.py +1523 -0
- agent_evolve/integrations/pydantic_ai/provider_free_calibrated_runner.py +607 -0
- agent_evolve/integrations/pydantic_ai/queued_runner.py +2634 -0
- agent_evolve/integrations/pydantic_ai/reconciled_residual_reachability.py +1417 -0
- agent_evolve/integrations/pydantic_ai/residual_forecast_geometry.py +445 -0
- agent_evolve/integrations/pydantic_ai/residual_reachability.py +674 -0
- agent_evolve/integrations/pydantic_ai/residual_semantic_cells.py +239 -0
- agent_evolve/integrations/pydantic_ai/sealed_output_replay.py +1068 -0
- agent_evolve/integrations/pydantic_ai/semantic_coverage_residual_portfolio.py +770 -0
- agent_evolve/integrations/pydantic_ai/semantic_decision_replay.py +383 -0
- agent_evolve/integrations/pydantic_ai/support_adaptive_residual_portfolio.py +135 -0
- agent_evolve/integrations/pydantic_ai/trusted_residual_prompt_context.py +143 -0
- agent_evolve/integrations/pydantic_ai/validated_openrouter_model.py +107 -0
- agent_evolve/integrations/pymoo_adapter.py +242 -0
- agent_evolve/policies/__init__.py +17 -0
- agent_evolve/policies/check.py +469 -0
- agent_evolve/policies/emit_scaffold.py +451 -0
- agent_evolve/policies/feedback/__init__.py +37 -0
- agent_evolve/policies/feedback/held_out_asn.py +1325 -0
- agent_evolve/policies/genetic.py +607 -0
- agent_evolve/policies/llm_backoff.py +183 -0
- agent_evolve/policies/llm_chooser.py +226 -0
- agent_evolve/policies/llm_generator.py +1760 -0
- agent_evolve/policies/llm_init.py +267 -0
- agent_evolve/policies/llm_operator.py +109 -0
- agent_evolve/policies/llm_prior.py +194 -0
- agent_evolve/policies/llm_surrogate.py +334 -0
- agent_evolve/policies/measurement_evidence.py +704 -0
- agent_evolve/policies/memory/__init__.py +223 -0
- agent_evolve/policies/memory/balanced_subset_blocks.py +707 -0
- agent_evolve/policies/memory/compatibility_matching.py +593 -0
- agent_evolve/policies/memory/global_falsification.py +1841 -0
- agent_evolve/policies/memory/prompt_shape.py +503 -0
- agent_evolve/policies/memory/randomized_subset.py +714 -0
- agent_evolve/policies/memory/staged_causal.py +1270 -0
- agent_evolve/policies/memory/treatment_compliance.py +759 -0
- agent_evolve/policies/objective_resolution/__init__.py +17 -0
- agent_evolve/policies/objective_resolution/fixed_grid.py +364 -0
- agent_evolve/policies/operator_portfolio.py +407 -0
- agent_evolve/policies/reguidance.py +1133 -0
- agent_evolve/policies/reward/__init__.py +83 -0
- agent_evolve/policies/reward/affine_candidate_consequence.py +156 -0
- agent_evolve/policies/reward/affine_candidate_consequence_3d.py +159 -0
- agent_evolve/policies/reward/affine_hypervolume.py +490 -0
- agent_evolve/policies/reward/affine_hypervolume_3d.py +567 -0
- agent_evolve/policies/reward/contextual_marginal_utility.py +318 -0
- agent_evolve/policies/reward/frozen_archive.py +360 -0
- agent_evolve/policies/reward/frozen_wave_archive.py +368 -0
- agent_evolve/policies/search_state.py +208 -0
- agent_evolve/policies/selection/__init__.py +345 -0
- agent_evolve/policies/selection/acquisition_certified_slate.py +684 -0
- agent_evolve/policies/selection/affine_frontier_context.py +330 -0
- agent_evolve/policies/selection/affine_frontier_target.py +473 -0
- agent_evolve/policies/selection/archive_elite.py +1346 -0
- agent_evolve/policies/selection/calibrated_portfolio_binding.py +640 -0
- agent_evolve/policies/selection/calibrated_slate.py +1394 -0
- agent_evolve/policies/selection/calibrated_slate_codec.py +579 -0
- agent_evolve/policies/selection/common_candidate_pool.py +685 -0
- agent_evolve/policies/selection/diagnostic_sampling.py +319 -0
- agent_evolve/policies/selection/disjoint_pairs.py +479 -0
- agent_evolve/policies/selection/elite_explorer.py +719 -0
- agent_evolve/policies/selection/finite_action.py +187 -0
- agent_evolve/policies/selection/finite_option_prompt_projection.py +377 -0
- agent_evolve/policies/selection/finite_palette_evidence.py +247 -0
- agent_evolve/policies/selection/forecast_calibration.py +922 -0
- agent_evolve/policies/selection/frontier_probe_slate.py +814 -0
- agent_evolve/policies/selection/frozen_archive_pairs.py +762 -0
- agent_evolve/policies/selection/full_support_slate.py +91 -0
- agent_evolve/policies/selection/meaningful_direction.py +240 -0
- agent_evolve/policies/selection/memory_dose_feasibility.py +259 -0
- agent_evolve/policies/selection/model_anchored_slate.py +826 -0
- agent_evolve/policies/selection/phenotype_recourse.py +979 -0
- agent_evolve/policies/selection/proposal_support.py +368 -0
- agent_evolve/policies/selection/random_portfolio.py +254 -0
- agent_evolve/policies/selection/regret_bounded_slate.py +1084 -0
- agent_evolve/policies/selection/residual_frontier.py +463 -0
- agent_evolve/policies/selection/residual_frontier_target.py +605 -0
- agent_evolve/policies/selection/structural_posterior_slate.py +1571 -0
- agent_evolve/policies/selection/target_conditioned_allocator.py +648 -0
- agent_evolve/policies/selection/target_conditioned_features.py +812 -0
- agent_evolve/policies/selection/target_conditioned_prequential.py +1527 -0
- agent_evolve/policies/selection/task_keyed_palette.py +906 -0
- agent_evolve/policies/semantics.py +147 -0
- agent_evolve/policies/structure.py +362 -0
- agent_evolve/policies/structured_output_budget.py +62 -0
- agent_evolve/policies/surrogate.py +696 -0
- agent_evolve/policies/variation/__init__.py +1 -0
- agent_evolve/policies/variation/compositional_finite_catalog.py +426 -0
- agent_evolve/policies/variation/crossover_inheritance.py +575 -0
- agent_evolve/policies/variation/disjoint_recombination.py +611 -0
- agent_evolve/policies/variation/exact_composition_capacity.py +214 -0
- agent_evolve/policies/variation/exact_parent_crossover.py +950 -0
- agent_evolve/policies/variation/multiscale_restart_catalog.py +372 -0
- agent_evolve/policies/variation/source_union_finite_catalog.py +403 -0
- agent_evolve/policies/variation/typed_patch.py +1981 -0
- agent_evolve/policies/weighted_prior.py +394 -0
- agent_evolve/ports/__init__.py +383 -0
- agent_evolve/ports/action_allocation.py +733 -0
- agent_evolve/ports/action_allocation_frame.py +1153 -0
- agent_evolve/ports/action_allocation_frame_commit.py +294 -0
- agent_evolve/ports/action_allocation_frame_commit_v3.py +432 -0
- agent_evolve/ports/action_allocation_frame_v3.py +995 -0
- agent_evolve/ports/action_forecast.py +1568 -0
- agent_evolve/ports/action_metric_projection.py +165 -0
- agent_evolve/ports/agentic_generator.py +1561 -0
- agent_evolve/ports/archive_context.py +136 -0
- agent_evolve/ports/artifact_sanitizer.py +44 -0
- agent_evolve/ports/artifact_store.py +225 -0
- agent_evolve/ports/clock.py +13 -0
- agent_evolve/ports/contextual_search_allocation.py +827 -0
- agent_evolve/ports/decision_metric_projection.py +258 -0
- agent_evolve/ports/event_store.py +55 -0
- agent_evolve/ports/executable_hypothesis.py +557 -0
- agent_evolve/ports/finite_acquisition.py +377 -0
- agent_evolve/ports/finite_acquisition_batch.py +296 -0
- agent_evolve/ports/finite_acquisition_batch_json.py +164 -0
- agent_evolve/ports/finite_acquisition_json.py +247 -0
- agent_evolve/ports/finite_acquisition_space.py +168 -0
- agent_evolve/ports/finite_action_selection.py +348 -0
- agent_evolve/ports/finite_action_set.py +256 -0
- agent_evolve/ports/frontier_target.py +396 -0
- agent_evolve/ports/generation_failure.py +43 -0
- agent_evolve/ports/hard_feasibility.py +233 -0
- agent_evolve/ports/id_factory.py +34 -0
- agent_evolve/ports/llm_task_queue.py +93 -0
- agent_evolve/ports/objective_resolution.py +419 -0
- agent_evolve/ports/paired_allocation_comparison.py +401 -0
- agent_evolve/ports/paired_block_schedule.py +475 -0
- agent_evolve/ports/parent_measurement.py +336 -0
- agent_evolve/ports/portfolio_memory_dose.py +643 -0
- agent_evolve/ports/portfolio_selection.py +3169 -0
- agent_evolve/ports/postcommit_rank_authority.py +467 -0
- agent_evolve/ports/presented_action_evidence.py +794 -0
- agent_evolve/ports/resource_lease.py +162 -0
- agent_evolve/ports/structured_generator.py +734 -0
- agent_evolve/ports/structured_output_budget.py +120 -0
- agent_evolve/ports/subprocess_boundary.py +138 -0
- agent_evolve/ports/treatment_assignment.py +466 -0
- agent_evolve/ports/variation_catalog.py +76 -0
- agent_evolve/ports/variation_source.py +226 -0
- agent_evolve/proposal_mode.py +157 -0
- agent_evolve/proposers/__init__.py +10 -0
- agent_evolve/proposers/random_proposer.py +188 -0
- agent_evolve/provider_accounting.py +163 -0
- agent_evolve/py.typed +0 -0
- agent_evolve/reference_method.py +1570 -0
- agent_evolve/session/__init__.py +11 -0
- agent_evolve/session/authorship.py +864 -0
- agent_evolve/session/evaluate.py +236 -0
- agent_evolve/session/fidelity.py +237 -0
- agent_evolve/session/genetic_loop.py +742 -0
- agent_evolve/session/loop.py +803 -0
- agent_evolve/session/screening.py +671 -0
- agent_evolve/settings.py +376 -0
- agent_evolve/workload_kit.py +368 -0
- agent_evolve/workload_prompt.py +398 -0
- agentevolve_optimizer-0.5.0.dist-info/METADATA +599 -0
- agentevolve_optimizer-0.5.0.dist-info/RECORD +414 -0
- agentevolve_optimizer-0.5.0.dist-info/WHEEL +5 -0
- agentevolve_optimizer-0.5.0.dist-info/entry_points.txt +2 -0
- agentevolve_optimizer-0.5.0.dist-info/licenses/LICENSE +21 -0
- agentevolve_optimizer-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1131 @@
|
|
|
1
|
+
"""Benchmark-inverted execution of the post-reflection AgentEvolve stage.
|
|
2
|
+
|
|
3
|
+
This module deliberately starts at the first point at which a benchmark has
|
|
4
|
+
finished its outcome-blind G1 sample and the reflection workflow has projected
|
|
5
|
+
scientific M/P views. The benchmark supplies those prepared forecast requests,
|
|
6
|
+
an identified set utility, and an identified evaluator. Trusted framework code
|
|
7
|
+
then performs the part that must be identical across every problem domain:
|
|
8
|
+
|
|
9
|
+
* run the M/P/N all-option forecasts concurrently;
|
|
10
|
+
* allocate a portfolio independently in each arm while excluding every G1 arm;
|
|
11
|
+
* evaluate G2 concurrently under an explicit, receipt-bound reuse policy; and
|
|
12
|
+
* synchronously cross an optional-or-required durability/interception boundary
|
|
13
|
+
after every hash-bound phase and before post-decision evaluation authority.
|
|
14
|
+
|
|
15
|
+
No benchmark metric, configuration schema, evaluator runtime, provider, or
|
|
16
|
+
prompt framework is imported here. A later outer workflow can compose G1 and
|
|
17
|
+
strict batched reflection around this service without changing this boundary.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import asyncio
|
|
23
|
+
import hashlib
|
|
24
|
+
import inspect
|
|
25
|
+
import json
|
|
26
|
+
import re
|
|
27
|
+
from collections.abc import Awaitable
|
|
28
|
+
from dataclasses import dataclass, field
|
|
29
|
+
from decimal import Decimal
|
|
30
|
+
from enum import Enum
|
|
31
|
+
from typing import Protocol, runtime_checkable
|
|
32
|
+
|
|
33
|
+
from agent_evolve.domain.finite_variation import (
|
|
34
|
+
FiniteVariationContract,
|
|
35
|
+
FiniteVariationOption,
|
|
36
|
+
validate_finite_variation_contract,
|
|
37
|
+
validate_finite_variation_option,
|
|
38
|
+
)
|
|
39
|
+
from agent_evolve.domain.ids import RunId
|
|
40
|
+
from agent_evolve.domain.patch import require_sha256
|
|
41
|
+
from agent_evolve.domain.typed_json import (
|
|
42
|
+
FrozenJsonObject,
|
|
43
|
+
freeze_json,
|
|
44
|
+
thaw_json,
|
|
45
|
+
typed_json_sha256,
|
|
46
|
+
)
|
|
47
|
+
from agent_evolve.ports.action_allocation import (
|
|
48
|
+
ActionAllocationRequest,
|
|
49
|
+
ActionAllocationResult,
|
|
50
|
+
DeterministicActionAllocator,
|
|
51
|
+
ForecastPortfolioUtilityBinding,
|
|
52
|
+
validate_action_portfolio_decision,
|
|
53
|
+
)
|
|
54
|
+
from agent_evolve.ports.action_forecast import (
|
|
55
|
+
ActionForecastEvidenceMode,
|
|
56
|
+
ActionForecastPolicy,
|
|
57
|
+
ActionForecastRequest,
|
|
58
|
+
ActionForecastResult,
|
|
59
|
+
validate_resolved_action_forecasts,
|
|
60
|
+
)
|
|
61
|
+
from agent_evolve.ports.agentic_generator import AgenticCallTelemetry
|
|
62
|
+
from agent_evolve.ports.portfolio_selection import (
|
|
63
|
+
PortfolioExperimentalArm,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
68
|
+
_OPTION_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,255}$")
|
|
69
|
+
_REQUEST_DOMAIN = b"agent-evolve:prepared-two-stage-action-request:v1\x00"
|
|
70
|
+
_EVALUATION_REQUEST_DOMAIN = b"agent-evolve:finite-action-evaluation-request:v1\x00"
|
|
71
|
+
_EVALUATION_RESULT_DOMAIN = b"agent-evolve:finite-action-evaluation-result:v1\x00"
|
|
72
|
+
_PHASE_RECEIPT_DOMAIN = b"agent-evolve:two-stage-phase-receipt:v1\x00"
|
|
73
|
+
_RESULT_DOMAIN = b"agent-evolve:prepared-two-stage-action-result:v1\x00"
|
|
74
|
+
ACTION_EVALUATION_REUSE_POLICY_ID = "action_evaluation_reuse"
|
|
75
|
+
ACTION_EVALUATION_REUSE_POLICY_VERSION = 1
|
|
76
|
+
ACTION_EVALUATION_REUSE_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
77
|
+
b"agent-evolve:action-evaluation-reuse:v1:"
|
|
78
|
+
b"per_arm=evaluate-each-arm-member-with-no-cross-arm-reuse;"
|
|
79
|
+
b"unique_action=evaluate-each-contract-action-once-and-bind-all-selecting-arms"
|
|
80
|
+
).hexdigest()
|
|
81
|
+
DURABLE_PHASE_COMMIT_POLICY_ID = "durable_phase_commit"
|
|
82
|
+
DURABLE_PHASE_COMMIT_POLICY_VERSION = 1
|
|
83
|
+
DURABLE_PHASE_COMMIT_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
84
|
+
b"agent-evolve:durable-phase-commit:v1:"
|
|
85
|
+
b"optional=phase-receipts-are-produced-and-a-supplied-sink-must-succeed;"
|
|
86
|
+
b"required=a-sink-must-be-present-and-each-phase-commit-must-complete-before-next-phase;"
|
|
87
|
+
b"allocation-commit-completes-before-evaluator-capability-is-used"
|
|
88
|
+
).hexdigest()
|
|
89
|
+
|
|
90
|
+
SCIENTIFIC_ARM_ORDER = (
|
|
91
|
+
PortfolioExperimentalArm.MEMORY,
|
|
92
|
+
PortfolioExperimentalArm.PERMUTED_PLACEBO,
|
|
93
|
+
PortfolioExperimentalArm.NEUTRAL,
|
|
94
|
+
)
|
|
95
|
+
_ARM_INDEX = {arm: index for index, arm in enumerate(SCIENTIFIC_ARM_ORDER)}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _canonical_json(value: object) -> bytes:
|
|
99
|
+
return json.dumps(
|
|
100
|
+
value,
|
|
101
|
+
allow_nan=False,
|
|
102
|
+
ensure_ascii=True,
|
|
103
|
+
separators=(",", ":"),
|
|
104
|
+
sort_keys=True,
|
|
105
|
+
).encode("ascii")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
109
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _agentic_call_telemetry_record(
|
|
113
|
+
telemetry: AgenticCallTelemetry | None,
|
|
114
|
+
) -> dict[str, object] | None:
|
|
115
|
+
"""Project provider telemetry without lossy Decimal-to-float conversion."""
|
|
116
|
+
|
|
117
|
+
if telemetry is None:
|
|
118
|
+
return None
|
|
119
|
+
if type(telemetry) is not AgenticCallTelemetry:
|
|
120
|
+
raise TypeError("telemetry must be exact AgenticCallTelemetry or None")
|
|
121
|
+
telemetry.__post_init__()
|
|
122
|
+
for name in ("provider_response_id", "finish_reason"):
|
|
123
|
+
value = getattr(telemetry, name)
|
|
124
|
+
if value is not None and type(value) is not str:
|
|
125
|
+
raise TypeError(f"telemetry {name} must be an exact string or None")
|
|
126
|
+
if telemetry.cost_usd is not None:
|
|
127
|
+
if type(telemetry.cost_usd) is not Decimal:
|
|
128
|
+
raise TypeError("telemetry cost_usd must be an exact Decimal or None")
|
|
129
|
+
if not telemetry.cost_usd.is_finite():
|
|
130
|
+
raise ValueError("telemetry cost_usd must be finite or None")
|
|
131
|
+
cost_usd: str | None = str(telemetry.cost_usd)
|
|
132
|
+
else:
|
|
133
|
+
cost_usd = None
|
|
134
|
+
return {
|
|
135
|
+
"requested_model": telemetry.requested_model,
|
|
136
|
+
"resolved_model": telemetry.resolved_model,
|
|
137
|
+
"resolved_provider": telemetry.resolved_provider,
|
|
138
|
+
"provider_response_id": telemetry.provider_response_id,
|
|
139
|
+
"finish_reason": telemetry.finish_reason,
|
|
140
|
+
"input_tokens": telemetry.input_tokens,
|
|
141
|
+
"output_tokens": telemetry.output_tokens,
|
|
142
|
+
"reasoning_tokens": telemetry.reasoning_tokens,
|
|
143
|
+
"cache_read_tokens": telemetry.cache_read_tokens,
|
|
144
|
+
"cache_write_tokens": telemetry.cache_write_tokens,
|
|
145
|
+
# Decimal is encoded as its exact canonical text, never a binary float.
|
|
146
|
+
"cost_usd": cost_usd,
|
|
147
|
+
"latency_ns": telemetry.latency_ns,
|
|
148
|
+
"attempt_count": telemetry.attempt_count,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True, slots=True)
|
|
153
|
+
class ActionForecastArmPlan:
|
|
154
|
+
"""One fully prepared scientific forecast call."""
|
|
155
|
+
|
|
156
|
+
arm: PortfolioExperimentalArm
|
|
157
|
+
request: ActionForecastRequest
|
|
158
|
+
|
|
159
|
+
def __post_init__(self) -> None:
|
|
160
|
+
if type(self.arm) is not PortfolioExperimentalArm:
|
|
161
|
+
raise TypeError("arm must be an exact PortfolioExperimentalArm")
|
|
162
|
+
if type(self.request) is not ActionForecastRequest:
|
|
163
|
+
raise TypeError("request must be an exact ActionForecastRequest")
|
|
164
|
+
self.request.__post_init__()
|
|
165
|
+
receipt = self.request.experimental_view_receipt
|
|
166
|
+
if self.arm is PortfolioExperimentalArm.MEMORY:
|
|
167
|
+
if self.request.evidence_mode is not ActionForecastEvidenceMode.GROUNDED:
|
|
168
|
+
raise ValueError("M must be a grounded forecast request")
|
|
169
|
+
if receipt is None or receipt.arm is not PortfolioExperimentalArm.MEMORY:
|
|
170
|
+
raise ValueError("M must carry a MEMORY experimental-view receipt")
|
|
171
|
+
elif self.arm is PortfolioExperimentalArm.PERMUTED_PLACEBO:
|
|
172
|
+
if self.request.evidence_mode is not ActionForecastEvidenceMode.GROUNDED:
|
|
173
|
+
raise ValueError("P must be a grounded forecast request")
|
|
174
|
+
if (
|
|
175
|
+
receipt is None
|
|
176
|
+
or receipt.arm is not PortfolioExperimentalArm.PERMUTED_PLACEBO
|
|
177
|
+
):
|
|
178
|
+
raise ValueError(
|
|
179
|
+
"P must carry a PERMUTED_PLACEBO experimental-view receipt"
|
|
180
|
+
)
|
|
181
|
+
else:
|
|
182
|
+
if self.request.evidence_mode is not ActionForecastEvidenceMode.CATALOG_ONLY:
|
|
183
|
+
raise ValueError("N must be a catalog-only forecast request")
|
|
184
|
+
if receipt is not None:
|
|
185
|
+
raise ValueError("N cannot carry an experimental-view receipt")
|
|
186
|
+
|
|
187
|
+
def to_record(self) -> dict[str, object]:
|
|
188
|
+
self.__post_init__()
|
|
189
|
+
return {"arm": self.arm.value, "request_sha256": self.request.request_sha256}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@runtime_checkable
|
|
193
|
+
class FiniteActionEvaluator(Protocol):
|
|
194
|
+
"""Benchmark-owned asynchronous evaluation of one sealed child."""
|
|
195
|
+
|
|
196
|
+
async def evaluate(
|
|
197
|
+
self,
|
|
198
|
+
request: "FiniteActionEvaluationRequest",
|
|
199
|
+
) -> FrozenJsonObject: ...
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass(frozen=True, slots=True)
|
|
203
|
+
class FiniteActionEvaluatorBinding:
|
|
204
|
+
"""Identified benchmark evaluator injected through the application boundary."""
|
|
205
|
+
|
|
206
|
+
evaluator: FiniteActionEvaluator = field(repr=False, compare=False)
|
|
207
|
+
evaluator_id: str
|
|
208
|
+
evaluator_version: int
|
|
209
|
+
definition_sha256: str
|
|
210
|
+
|
|
211
|
+
def __post_init__(self) -> None:
|
|
212
|
+
if not callable(getattr(self.evaluator, "evaluate", None)):
|
|
213
|
+
raise TypeError("evaluator must expose an async evaluate method")
|
|
214
|
+
if type(self.evaluator_id) is not str or _TOKEN.fullmatch(
|
|
215
|
+
self.evaluator_id
|
|
216
|
+
) is None:
|
|
217
|
+
raise ValueError("evaluator_id must use the closed token grammar")
|
|
218
|
+
if type(self.evaluator_version) is not int or self.evaluator_version <= 0:
|
|
219
|
+
raise ValueError("evaluator_version must be a positive exact integer")
|
|
220
|
+
require_sha256(self.definition_sha256, "definition_sha256")
|
|
221
|
+
|
|
222
|
+
def to_record(self) -> dict[str, object]:
|
|
223
|
+
self.__post_init__()
|
|
224
|
+
return {
|
|
225
|
+
"evaluator_id": self.evaluator_id,
|
|
226
|
+
"evaluator_version": self.evaluator_version,
|
|
227
|
+
"definition_sha256": self.definition_sha256,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class ActionEvaluationReuseMode(str, Enum):
|
|
232
|
+
"""Whether identical G2 actions may share evaluation across study arms."""
|
|
233
|
+
|
|
234
|
+
PER_ARM = "per_arm"
|
|
235
|
+
UNIQUE_ACTION = "unique_action"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass(frozen=True, slots=True)
|
|
239
|
+
class ActionEvaluationReusePolicyBinding:
|
|
240
|
+
"""Identified compute-accounting policy for cross-arm evaluation reuse."""
|
|
241
|
+
|
|
242
|
+
mode: ActionEvaluationReuseMode
|
|
243
|
+
policy_id: str = ACTION_EVALUATION_REUSE_POLICY_ID
|
|
244
|
+
policy_version: int = ACTION_EVALUATION_REUSE_POLICY_VERSION
|
|
245
|
+
definition_sha256: str = ACTION_EVALUATION_REUSE_POLICY_DEFINITION_SHA256
|
|
246
|
+
|
|
247
|
+
def __post_init__(self) -> None:
|
|
248
|
+
if type(self.mode) is not ActionEvaluationReuseMode:
|
|
249
|
+
raise TypeError("mode must be an exact ActionEvaluationReuseMode")
|
|
250
|
+
if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
|
|
251
|
+
raise ValueError("policy_id must use the closed token grammar")
|
|
252
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
253
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
254
|
+
require_sha256(self.definition_sha256, "definition_sha256")
|
|
255
|
+
|
|
256
|
+
def to_record(self) -> dict[str, object]:
|
|
257
|
+
self.__post_init__()
|
|
258
|
+
return {
|
|
259
|
+
"mode": self.mode.value,
|
|
260
|
+
"policy_id": self.policy_id,
|
|
261
|
+
"policy_version": self.policy_version,
|
|
262
|
+
"definition_sha256": self.definition_sha256,
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def per_arm_evaluation_reuse_policy() -> ActionEvaluationReusePolicyBinding:
|
|
267
|
+
"""Return the fail-safe default: no compute reuse across scientific arms."""
|
|
268
|
+
|
|
269
|
+
return ActionEvaluationReusePolicyBinding(ActionEvaluationReuseMode.PER_ARM)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class DurablePhaseCommitRequirement(str, Enum):
|
|
273
|
+
"""Whether a run may proceed without a durable phase-commit sink."""
|
|
274
|
+
|
|
275
|
+
OPTIONAL = "optional"
|
|
276
|
+
REQUIRED = "required"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@dataclass(frozen=True, slots=True)
|
|
280
|
+
class DurablePhaseCommitPolicyBinding:
|
|
281
|
+
"""Identified interception policy bound into the scientific run request."""
|
|
282
|
+
|
|
283
|
+
requirement: DurablePhaseCommitRequirement
|
|
284
|
+
policy_id: str = DURABLE_PHASE_COMMIT_POLICY_ID
|
|
285
|
+
policy_version: int = DURABLE_PHASE_COMMIT_POLICY_VERSION
|
|
286
|
+
definition_sha256: str = DURABLE_PHASE_COMMIT_POLICY_DEFINITION_SHA256
|
|
287
|
+
|
|
288
|
+
def __post_init__(self) -> None:
|
|
289
|
+
if type(self.requirement) is not DurablePhaseCommitRequirement:
|
|
290
|
+
raise TypeError(
|
|
291
|
+
"requirement must be an exact DurablePhaseCommitRequirement"
|
|
292
|
+
)
|
|
293
|
+
if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
|
|
294
|
+
raise ValueError("policy_id must use the closed token grammar")
|
|
295
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
296
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
297
|
+
require_sha256(self.definition_sha256, "definition_sha256")
|
|
298
|
+
|
|
299
|
+
def to_record(self) -> dict[str, object]:
|
|
300
|
+
self.__post_init__()
|
|
301
|
+
return {
|
|
302
|
+
"requirement": self.requirement.value,
|
|
303
|
+
"policy_id": self.policy_id,
|
|
304
|
+
"policy_version": self.policy_version,
|
|
305
|
+
"definition_sha256": self.definition_sha256,
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def optional_phase_commit_policy() -> DurablePhaseCommitPolicyBinding:
|
|
310
|
+
"""Return the simple-use policy under which a commit sink may be omitted."""
|
|
311
|
+
|
|
312
|
+
return DurablePhaseCommitPolicyBinding(DurablePhaseCommitRequirement.OPTIONAL)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def required_scientific_phase_commit_policy() -> DurablePhaseCommitPolicyBinding:
|
|
316
|
+
"""Return the fail-closed policy for prospective scientific execution."""
|
|
317
|
+
|
|
318
|
+
return DurablePhaseCommitPolicyBinding(DurablePhaseCommitRequirement.REQUIRED)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@dataclass(frozen=True, slots=True)
|
|
322
|
+
class PreparedTwoStageActionEvolutionRequest:
|
|
323
|
+
"""Prepared G1/reflection outputs and policies for one generic M/P/N run."""
|
|
324
|
+
|
|
325
|
+
run_id: RunId
|
|
326
|
+
arm_plans: tuple[ActionForecastArmPlan, ...]
|
|
327
|
+
g1_option_ids: tuple[str, ...]
|
|
328
|
+
portfolio_size: int
|
|
329
|
+
utility: ForecastPortfolioUtilityBinding
|
|
330
|
+
evaluator: FiniteActionEvaluatorBinding
|
|
331
|
+
evaluation_context: FrozenJsonObject
|
|
332
|
+
evaluation_reuse: ActionEvaluationReusePolicyBinding = field(
|
|
333
|
+
default_factory=per_arm_evaluation_reuse_policy
|
|
334
|
+
)
|
|
335
|
+
phase_commit_policy: DurablePhaseCommitPolicyBinding = field(
|
|
336
|
+
default_factory=optional_phase_commit_policy
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def __post_init__(self) -> None:
|
|
340
|
+
if type(self.run_id) is not RunId:
|
|
341
|
+
raise TypeError("run_id must be an exact RunId")
|
|
342
|
+
RunId.__post_init__(self.run_id)
|
|
343
|
+
if type(self.arm_plans) is not tuple or any(
|
|
344
|
+
type(value) is not ActionForecastArmPlan for value in self.arm_plans
|
|
345
|
+
):
|
|
346
|
+
raise TypeError("arm_plans must be an exact ActionForecastArmPlan tuple")
|
|
347
|
+
for plan in self.arm_plans:
|
|
348
|
+
plan.__post_init__()
|
|
349
|
+
if tuple(plan.arm for plan in self.arm_plans) != SCIENTIFIC_ARM_ORDER:
|
|
350
|
+
raise ValueError("arm_plans must contain canonical M/P/N order exactly")
|
|
351
|
+
requests = tuple(plan.request for plan in self.arm_plans)
|
|
352
|
+
baseline = requests[0]
|
|
353
|
+
common = (
|
|
354
|
+
baseline.operation,
|
|
355
|
+
baseline.instruction,
|
|
356
|
+
baseline.context_sha256,
|
|
357
|
+
baseline.optimization_semantics.semantics_id,
|
|
358
|
+
baseline.optimization_semantics.semantics_version,
|
|
359
|
+
baseline.optimization_semantics.definition_sha256,
|
|
360
|
+
baseline.finite_variation_contract.identity_sha256,
|
|
361
|
+
baseline.parent_metric_values,
|
|
362
|
+
baseline.metric_scales,
|
|
363
|
+
baseline.max_output_tokens,
|
|
364
|
+
baseline.temperature,
|
|
365
|
+
)
|
|
366
|
+
for candidate in requests[1:]:
|
|
367
|
+
candidate_common = (
|
|
368
|
+
candidate.operation,
|
|
369
|
+
candidate.instruction,
|
|
370
|
+
candidate.context_sha256,
|
|
371
|
+
candidate.optimization_semantics.semantics_id,
|
|
372
|
+
candidate.optimization_semantics.semantics_version,
|
|
373
|
+
candidate.optimization_semantics.definition_sha256,
|
|
374
|
+
candidate.finite_variation_contract.identity_sha256,
|
|
375
|
+
candidate.parent_metric_values,
|
|
376
|
+
candidate.metric_scales,
|
|
377
|
+
candidate.max_output_tokens,
|
|
378
|
+
candidate.temperature,
|
|
379
|
+
)
|
|
380
|
+
if candidate_common != common:
|
|
381
|
+
raise ValueError(
|
|
382
|
+
"M/P/N may differ only in call identity and evidence treatment"
|
|
383
|
+
)
|
|
384
|
+
if len({request.call_id for request in requests}) != len(requests):
|
|
385
|
+
raise ValueError("M/P/N require distinct logical call IDs")
|
|
386
|
+
memory_registry = requests[0].source_registry
|
|
387
|
+
placebo_registry = requests[1].source_registry
|
|
388
|
+
assert memory_registry is not None and placebo_registry is not None
|
|
389
|
+
if memory_registry.registry_sha256 != placebo_registry.registry_sha256:
|
|
390
|
+
raise ValueError("M and P must use the same admitted source registry")
|
|
391
|
+
|
|
392
|
+
if type(self.g1_option_ids) is not tuple or any(
|
|
393
|
+
type(value) is not str or _OPTION_ID.fullmatch(value) is None
|
|
394
|
+
for value in self.g1_option_ids
|
|
395
|
+
):
|
|
396
|
+
raise TypeError("g1_option_ids must be an exact option-ID tuple")
|
|
397
|
+
if not self.g1_option_ids:
|
|
398
|
+
raise ValueError("g1_option_ids must be non-empty")
|
|
399
|
+
if self.g1_option_ids != tuple(sorted(set(self.g1_option_ids))):
|
|
400
|
+
raise ValueError("g1_option_ids must be unique and canonical")
|
|
401
|
+
contract = baseline.finite_variation_contract
|
|
402
|
+
validate_finite_variation_contract(contract)
|
|
403
|
+
contract_ids = {option.option_id for option in contract.options}
|
|
404
|
+
if not set(self.g1_option_ids).issubset(contract_ids):
|
|
405
|
+
raise ValueError("g1_option_ids contains an option outside the contract")
|
|
406
|
+
eligible_count = len(contract_ids - set(self.g1_option_ids))
|
|
407
|
+
if type(self.portfolio_size) is not int or self.portfolio_size <= 0:
|
|
408
|
+
raise ValueError("portfolio_size must be a positive exact integer")
|
|
409
|
+
if self.portfolio_size > eligible_count:
|
|
410
|
+
raise ValueError("portfolio_size exceeds the non-G1 action count")
|
|
411
|
+
if type(self.utility) is not ForecastPortfolioUtilityBinding:
|
|
412
|
+
raise TypeError("utility must be an exact identified binding")
|
|
413
|
+
self.utility.__post_init__()
|
|
414
|
+
if type(self.evaluator) is not FiniteActionEvaluatorBinding:
|
|
415
|
+
raise TypeError("evaluator must be an exact identified binding")
|
|
416
|
+
self.evaluator.__post_init__()
|
|
417
|
+
if type(self.evaluation_context) is not FrozenJsonObject:
|
|
418
|
+
raise TypeError("evaluation_context must be an exact FrozenJsonObject")
|
|
419
|
+
if freeze_json(self.evaluation_context) is not self.evaluation_context:
|
|
420
|
+
raise TypeError("evaluation_context must already be frozen typed JSON")
|
|
421
|
+
if type(self.evaluation_reuse) is not ActionEvaluationReusePolicyBinding:
|
|
422
|
+
raise TypeError("evaluation_reuse must be an exact identified binding")
|
|
423
|
+
self.evaluation_reuse.__post_init__()
|
|
424
|
+
if type(self.phase_commit_policy) is not DurablePhaseCommitPolicyBinding:
|
|
425
|
+
raise TypeError("phase_commit_policy must be an exact identified binding")
|
|
426
|
+
self.phase_commit_policy.__post_init__()
|
|
427
|
+
|
|
428
|
+
@property
|
|
429
|
+
def finite_variation_contract(self) -> FiniteVariationContract:
|
|
430
|
+
self.__post_init__()
|
|
431
|
+
return self.arm_plans[0].request.finite_variation_contract
|
|
432
|
+
|
|
433
|
+
@property
|
|
434
|
+
def eligible_option_ids(self) -> tuple[str, ...]:
|
|
435
|
+
self.__post_init__()
|
|
436
|
+
excluded = set(self.g1_option_ids)
|
|
437
|
+
return tuple(
|
|
438
|
+
sorted(
|
|
439
|
+
option.option_id
|
|
440
|
+
for option in self.finite_variation_contract.options
|
|
441
|
+
if option.option_id not in excluded
|
|
442
|
+
)
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
def to_record(self) -> dict[str, object]:
|
|
446
|
+
self.__post_init__()
|
|
447
|
+
return {
|
|
448
|
+
"schema_version": 1,
|
|
449
|
+
"run_id": self.run_id.value,
|
|
450
|
+
"arm_plans": [plan.to_record() for plan in self.arm_plans],
|
|
451
|
+
"finite_contract_identity_sha256": (
|
|
452
|
+
self.finite_variation_contract.identity_sha256
|
|
453
|
+
),
|
|
454
|
+
"g1_option_ids": list(self.g1_option_ids),
|
|
455
|
+
"eligible_option_ids": list(self.eligible_option_ids),
|
|
456
|
+
"portfolio_size": self.portfolio_size,
|
|
457
|
+
"utility": self.utility.to_record(),
|
|
458
|
+
"evaluator": self.evaluator.to_record(),
|
|
459
|
+
"evaluation_context_sha256": typed_json_sha256(
|
|
460
|
+
self.evaluation_context
|
|
461
|
+
),
|
|
462
|
+
"evaluation_reuse": self.evaluation_reuse.to_record(),
|
|
463
|
+
"phase_commit_policy": self.phase_commit_policy.to_record(),
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
@property
|
|
467
|
+
def request_sha256(self) -> str:
|
|
468
|
+
return _hash(_REQUEST_DOMAIN, self.to_record())
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
@dataclass(frozen=True, slots=True)
|
|
472
|
+
class ActionForecastArmExecution:
|
|
473
|
+
arm: PortfolioExperimentalArm
|
|
474
|
+
request_sha256: str
|
|
475
|
+
result: ActionForecastResult
|
|
476
|
+
|
|
477
|
+
def __post_init__(self) -> None:
|
|
478
|
+
if type(self.arm) is not PortfolioExperimentalArm:
|
|
479
|
+
raise TypeError("arm must be exact")
|
|
480
|
+
require_sha256(self.request_sha256, "request_sha256")
|
|
481
|
+
if type(self.result) is not ActionForecastResult:
|
|
482
|
+
raise TypeError("result must be an exact ActionForecastResult")
|
|
483
|
+
self.result.__post_init__()
|
|
484
|
+
if self.result.forecasts.request_sha256 != self.request_sha256:
|
|
485
|
+
raise ValueError("forecast result is bound to a different arm request")
|
|
486
|
+
|
|
487
|
+
def to_record(self) -> dict[str, object]:
|
|
488
|
+
self.__post_init__()
|
|
489
|
+
return {
|
|
490
|
+
"arm": self.arm.value,
|
|
491
|
+
"request_sha256": self.request_sha256,
|
|
492
|
+
"forecast_receipt_sha256": self.result.forecasts.receipt_sha256,
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
@dataclass(frozen=True, slots=True)
|
|
497
|
+
class ActionAllocationArmExecution:
|
|
498
|
+
arm: PortfolioExperimentalArm
|
|
499
|
+
request: ActionAllocationRequest
|
|
500
|
+
result: ActionAllocationResult
|
|
501
|
+
|
|
502
|
+
def __post_init__(self) -> None:
|
|
503
|
+
if type(self.arm) is not PortfolioExperimentalArm:
|
|
504
|
+
raise TypeError("arm must be exact")
|
|
505
|
+
if type(self.request) is not ActionAllocationRequest:
|
|
506
|
+
raise TypeError("request must be exact ActionAllocationRequest")
|
|
507
|
+
self.request.__post_init__()
|
|
508
|
+
if type(self.result) is not ActionAllocationResult:
|
|
509
|
+
raise TypeError("result must be exact ActionAllocationResult")
|
|
510
|
+
self.result.__post_init__()
|
|
511
|
+
validate_action_portfolio_decision(self.request, self.result.decision)
|
|
512
|
+
|
|
513
|
+
def to_record(self) -> dict[str, object]:
|
|
514
|
+
self.__post_init__()
|
|
515
|
+
return {
|
|
516
|
+
"arm": self.arm.value,
|
|
517
|
+
"allocation_request_sha256": self.request.request_sha256,
|
|
518
|
+
"decision_receipt_sha256": self.result.decision.receipt_sha256,
|
|
519
|
+
"selected_option_ids": [
|
|
520
|
+
member.option_id for member in self.result.decision.members
|
|
521
|
+
],
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@dataclass(frozen=True, slots=True)
|
|
526
|
+
class FiniteActionEvaluationRequest:
|
|
527
|
+
run_id: RunId
|
|
528
|
+
finite_contract_identity_sha256: str
|
|
529
|
+
option: FiniteVariationOption
|
|
530
|
+
selected_by_arms: tuple[PortfolioExperimentalArm, ...]
|
|
531
|
+
context: FrozenJsonObject
|
|
532
|
+
|
|
533
|
+
def __post_init__(self) -> None:
|
|
534
|
+
if type(self.run_id) is not RunId:
|
|
535
|
+
raise TypeError("run_id must be exact")
|
|
536
|
+
RunId.__post_init__(self.run_id)
|
|
537
|
+
require_sha256(
|
|
538
|
+
self.finite_contract_identity_sha256,
|
|
539
|
+
"finite_contract_identity_sha256",
|
|
540
|
+
)
|
|
541
|
+
validate_finite_variation_option(self.option)
|
|
542
|
+
if type(self.selected_by_arms) is not tuple or not self.selected_by_arms:
|
|
543
|
+
raise ValueError("selected_by_arms must be a non-empty exact tuple")
|
|
544
|
+
if any(type(arm) is not PortfolioExperimentalArm for arm in self.selected_by_arms):
|
|
545
|
+
raise TypeError("selected_by_arms must contain exact arms")
|
|
546
|
+
if self.selected_by_arms != tuple(
|
|
547
|
+
sorted(set(self.selected_by_arms), key=_ARM_INDEX.__getitem__)
|
|
548
|
+
):
|
|
549
|
+
raise ValueError("selected_by_arms must be unique and canonical")
|
|
550
|
+
if type(self.context) is not FrozenJsonObject:
|
|
551
|
+
raise TypeError("context must be an exact FrozenJsonObject")
|
|
552
|
+
if freeze_json(self.context) is not self.context:
|
|
553
|
+
raise TypeError("context must already be frozen typed JSON")
|
|
554
|
+
|
|
555
|
+
def to_record(self) -> dict[str, object]:
|
|
556
|
+
self.__post_init__()
|
|
557
|
+
return {
|
|
558
|
+
"schema_version": 1,
|
|
559
|
+
"run_id": self.run_id.value,
|
|
560
|
+
"finite_contract_identity_sha256": self.finite_contract_identity_sha256,
|
|
561
|
+
"option": self.option.evidence_record(),
|
|
562
|
+
"selected_by_arms": [arm.value for arm in self.selected_by_arms],
|
|
563
|
+
"context_sha256": typed_json_sha256(self.context),
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
@property
|
|
567
|
+
def request_sha256(self) -> str:
|
|
568
|
+
return _hash(_EVALUATION_REQUEST_DOMAIN, self.to_record())
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
@dataclass(frozen=True, slots=True)
|
|
572
|
+
class FiniteActionEvaluationResult:
|
|
573
|
+
request: FiniteActionEvaluationRequest
|
|
574
|
+
outcome: FrozenJsonObject
|
|
575
|
+
evaluator_id: str
|
|
576
|
+
evaluator_version: int
|
|
577
|
+
evaluator_definition_sha256: str
|
|
578
|
+
|
|
579
|
+
def __post_init__(self) -> None:
|
|
580
|
+
if type(self.request) is not FiniteActionEvaluationRequest:
|
|
581
|
+
raise TypeError("request must be an exact FiniteActionEvaluationRequest")
|
|
582
|
+
self.request.__post_init__()
|
|
583
|
+
if type(self.outcome) is not FrozenJsonObject:
|
|
584
|
+
raise TypeError("outcome must be an exact FrozenJsonObject")
|
|
585
|
+
if freeze_json(self.outcome) is not self.outcome:
|
|
586
|
+
raise TypeError("outcome must already be frozen typed JSON")
|
|
587
|
+
if type(self.evaluator_id) is not str or _TOKEN.fullmatch(
|
|
588
|
+
self.evaluator_id
|
|
589
|
+
) is None:
|
|
590
|
+
raise ValueError("evaluator_id must use the closed token grammar")
|
|
591
|
+
if type(self.evaluator_version) is not int or self.evaluator_version <= 0:
|
|
592
|
+
raise ValueError("evaluator_version must be positive")
|
|
593
|
+
require_sha256(
|
|
594
|
+
self.evaluator_definition_sha256,
|
|
595
|
+
"evaluator_definition_sha256",
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
599
|
+
self.__post_init__()
|
|
600
|
+
return {
|
|
601
|
+
"schema_version": 1,
|
|
602
|
+
"evaluation_request_sha256": self.request.request_sha256,
|
|
603
|
+
"option_id": self.request.option.option_id,
|
|
604
|
+
"option_identity_sha256": self.request.option.identity_sha256,
|
|
605
|
+
"child_configuration_sha256": (
|
|
606
|
+
self.request.option.child_configuration_sha256
|
|
607
|
+
),
|
|
608
|
+
"selected_by_arms": [arm.value for arm in self.request.selected_by_arms],
|
|
609
|
+
"outcome_sha256": typed_json_sha256(self.outcome),
|
|
610
|
+
"evaluator": {
|
|
611
|
+
"evaluator_id": self.evaluator_id,
|
|
612
|
+
"evaluator_version": self.evaluator_version,
|
|
613
|
+
"definition_sha256": self.evaluator_definition_sha256,
|
|
614
|
+
},
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
@property
|
|
618
|
+
def receipt_sha256(self) -> str:
|
|
619
|
+
return _hash(_EVALUATION_RESULT_DOMAIN, self._unsigned_record())
|
|
620
|
+
|
|
621
|
+
def to_record(self) -> dict[str, object]:
|
|
622
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
class TwoStageActionPhase(str, Enum):
|
|
626
|
+
FORECAST = "forecast"
|
|
627
|
+
ALLOCATE = "allocate"
|
|
628
|
+
EVALUATE = "evaluate"
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
@dataclass(frozen=True, slots=True)
|
|
632
|
+
class TwoStageActionPhaseReceipt:
|
|
633
|
+
phase: TwoStageActionPhase
|
|
634
|
+
input_sha256: str
|
|
635
|
+
output_sha256: str
|
|
636
|
+
|
|
637
|
+
def __post_init__(self) -> None:
|
|
638
|
+
if type(self.phase) is not TwoStageActionPhase:
|
|
639
|
+
raise TypeError("phase must be an exact TwoStageActionPhase")
|
|
640
|
+
require_sha256(self.input_sha256, "input_sha256")
|
|
641
|
+
require_sha256(self.output_sha256, "output_sha256")
|
|
642
|
+
|
|
643
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
644
|
+
self.__post_init__()
|
|
645
|
+
return {
|
|
646
|
+
"schema_version": 1,
|
|
647
|
+
"phase": self.phase.value,
|
|
648
|
+
"input_sha256": self.input_sha256,
|
|
649
|
+
"output_sha256": self.output_sha256,
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
@property
|
|
653
|
+
def receipt_sha256(self) -> str:
|
|
654
|
+
return _hash(_PHASE_RECEIPT_DOMAIN, self._unsigned_record())
|
|
655
|
+
|
|
656
|
+
def to_record(self) -> dict[str, object]:
|
|
657
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
@dataclass(frozen=True, slots=True)
|
|
661
|
+
class TwoStageActionPhaseCommit:
|
|
662
|
+
"""Exact receipt/payload pair handed to an external durability boundary."""
|
|
663
|
+
|
|
664
|
+
receipt: TwoStageActionPhaseReceipt
|
|
665
|
+
payload: FrozenJsonObject
|
|
666
|
+
|
|
667
|
+
def __post_init__(self) -> None:
|
|
668
|
+
if type(self.receipt) is not TwoStageActionPhaseReceipt:
|
|
669
|
+
raise TypeError("receipt must be an exact TwoStageActionPhaseReceipt")
|
|
670
|
+
self.receipt.__post_init__()
|
|
671
|
+
if type(self.payload) is not FrozenJsonObject:
|
|
672
|
+
raise TypeError("payload must be an exact FrozenJsonObject")
|
|
673
|
+
if freeze_json(self.payload) is not self.payload:
|
|
674
|
+
raise TypeError("payload must already be frozen typed JSON")
|
|
675
|
+
if typed_json_sha256(self.payload) != self.receipt.output_sha256:
|
|
676
|
+
raise ValueError("phase payload differs from its hash-bound receipt")
|
|
677
|
+
|
|
678
|
+
def to_record(self) -> dict[str, object]:
|
|
679
|
+
self.__post_init__()
|
|
680
|
+
return {
|
|
681
|
+
"schema_version": 1,
|
|
682
|
+
"receipt": self.receipt.to_record(),
|
|
683
|
+
"payload_sha256": typed_json_sha256(self.payload),
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
@runtime_checkable
|
|
688
|
+
class TwoStageActionPhaseCommitSink(Protocol):
|
|
689
|
+
"""Durably commit one completed phase before the coordinator can continue."""
|
|
690
|
+
|
|
691
|
+
def commit(
|
|
692
|
+
self,
|
|
693
|
+
phase_commit: TwoStageActionPhaseCommit,
|
|
694
|
+
) -> Awaitable[None] | None: ...
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
class TwoStageActionPhaseCommitError(RuntimeError):
|
|
698
|
+
"""A required or supplied phase durability boundary did not complete."""
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _freeze_phase_payload(value: dict[str, object]) -> FrozenJsonObject:
|
|
702
|
+
frozen = freeze_json(value)
|
|
703
|
+
if type(frozen) is not FrozenJsonObject:
|
|
704
|
+
raise AssertionError("phase payload root must freeze as an object")
|
|
705
|
+
return frozen
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def _phase_commit(
|
|
709
|
+
*,
|
|
710
|
+
receipt: TwoStageActionPhaseReceipt,
|
|
711
|
+
payload: FrozenJsonObject,
|
|
712
|
+
) -> TwoStageActionPhaseCommit:
|
|
713
|
+
return TwoStageActionPhaseCommit(receipt=receipt, payload=payload)
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
async def _publish_phase_commit(
|
|
717
|
+
*,
|
|
718
|
+
policy: DurablePhaseCommitPolicyBinding,
|
|
719
|
+
sink: TwoStageActionPhaseCommitSink | None,
|
|
720
|
+
phase_commit: TwoStageActionPhaseCommit,
|
|
721
|
+
) -> None:
|
|
722
|
+
policy.__post_init__()
|
|
723
|
+
phase_commit.__post_init__()
|
|
724
|
+
if sink is None:
|
|
725
|
+
if policy.requirement is DurablePhaseCommitRequirement.REQUIRED:
|
|
726
|
+
raise TwoStageActionPhaseCommitError(
|
|
727
|
+
"required durable phase-commit sink is absent"
|
|
728
|
+
)
|
|
729
|
+
return
|
|
730
|
+
commit_method = getattr(sink, "commit", None)
|
|
731
|
+
if not callable(commit_method):
|
|
732
|
+
raise TypeError("phase_commit_sink must expose a commit method")
|
|
733
|
+
try:
|
|
734
|
+
result = commit_method(phase_commit)
|
|
735
|
+
if inspect.isawaitable(result):
|
|
736
|
+
result = await result
|
|
737
|
+
if result is not None:
|
|
738
|
+
raise TypeError("phase commit sinks must return None")
|
|
739
|
+
except Exception as exc:
|
|
740
|
+
raise TwoStageActionPhaseCommitError(
|
|
741
|
+
f"{phase_commit.receipt.phase.value} phase commit failed"
|
|
742
|
+
) from exc
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
async def _gather_all_settled(
|
|
746
|
+
awaitables: tuple[Awaitable[object], ...],
|
|
747
|
+
) -> tuple[object, ...]:
|
|
748
|
+
"""Await every sibling and then raise the first failure in input order.
|
|
749
|
+
|
|
750
|
+
Scientific multi-arm calls are an all-or-nothing stage. Waiting for every
|
|
751
|
+
submitted sibling gives the outer artifact recorder a complete physical-
|
|
752
|
+
attempt ledger, while input-order failure selection keeps the observable
|
|
753
|
+
error deterministic. A failed stage never yields partial results.
|
|
754
|
+
"""
|
|
755
|
+
|
|
756
|
+
tasks = tuple(asyncio.ensure_future(awaitable) for awaitable in awaitables)
|
|
757
|
+
try:
|
|
758
|
+
results = tuple(
|
|
759
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
760
|
+
)
|
|
761
|
+
except asyncio.CancelledError:
|
|
762
|
+
# Cancellation is not scientific partial success either. Explicitly
|
|
763
|
+
# cancel and settle every sibling so provider/evaluator cleanup and its
|
|
764
|
+
# physical-attempt journal finish before cancellation escapes.
|
|
765
|
+
for task in tasks:
|
|
766
|
+
if not task.done():
|
|
767
|
+
task.cancel()
|
|
768
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
769
|
+
raise
|
|
770
|
+
for result in results:
|
|
771
|
+
if isinstance(result, BaseException):
|
|
772
|
+
raise result
|
|
773
|
+
return results
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
777
|
+
class PreparedTwoStageActionEvolutionResult:
|
|
778
|
+
request_sha256: str
|
|
779
|
+
forecasts: tuple[ActionForecastArmExecution, ...]
|
|
780
|
+
allocations: tuple[ActionAllocationArmExecution, ...]
|
|
781
|
+
evaluations: tuple[FiniteActionEvaluationResult, ...]
|
|
782
|
+
evaluation_reuse: ActionEvaluationReusePolicyBinding
|
|
783
|
+
phase_commit_policy: DurablePhaseCommitPolicyBinding
|
|
784
|
+
phase_receipts: tuple[TwoStageActionPhaseReceipt, ...]
|
|
785
|
+
|
|
786
|
+
def __post_init__(self) -> None:
|
|
787
|
+
require_sha256(self.request_sha256, "request_sha256")
|
|
788
|
+
if tuple(value.arm for value in self.forecasts) != SCIENTIFIC_ARM_ORDER:
|
|
789
|
+
raise ValueError("forecasts must use canonical M/P/N order")
|
|
790
|
+
if tuple(value.arm for value in self.allocations) != SCIENTIFIC_ARM_ORDER:
|
|
791
|
+
raise ValueError("allocations must use canonical M/P/N order")
|
|
792
|
+
for value in self.forecasts:
|
|
793
|
+
value.__post_init__()
|
|
794
|
+
for value in self.allocations:
|
|
795
|
+
value.__post_init__()
|
|
796
|
+
if type(self.evaluations) is not tuple or not self.evaluations:
|
|
797
|
+
raise ValueError("evaluations must be a non-empty exact tuple")
|
|
798
|
+
for value in self.evaluations:
|
|
799
|
+
if type(value) is not FiniteActionEvaluationResult:
|
|
800
|
+
raise TypeError("evaluations must contain exact results")
|
|
801
|
+
value.__post_init__()
|
|
802
|
+
evaluation_ids = tuple(
|
|
803
|
+
value.request.request_sha256 for value in self.evaluations
|
|
804
|
+
)
|
|
805
|
+
if len(set(evaluation_ids)) != len(evaluation_ids):
|
|
806
|
+
raise ValueError("an exact G2 evaluation request may execute only once")
|
|
807
|
+
if type(self.evaluation_reuse) is not ActionEvaluationReusePolicyBinding:
|
|
808
|
+
raise TypeError("evaluation_reuse must be an exact identified binding")
|
|
809
|
+
self.evaluation_reuse.__post_init__()
|
|
810
|
+
if type(self.phase_commit_policy) is not DurablePhaseCommitPolicyBinding:
|
|
811
|
+
raise TypeError("phase_commit_policy must be an exact identified binding")
|
|
812
|
+
self.phase_commit_policy.__post_init__()
|
|
813
|
+
expected_phases = tuple(TwoStageActionPhase)
|
|
814
|
+
if tuple(value.phase for value in self.phase_receipts) != expected_phases:
|
|
815
|
+
raise ValueError("phase_receipts must use forecast/allocate/evaluate order")
|
|
816
|
+
for value in self.phase_receipts:
|
|
817
|
+
value.__post_init__()
|
|
818
|
+
|
|
819
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
820
|
+
self.__post_init__()
|
|
821
|
+
return {
|
|
822
|
+
"schema_version": 1,
|
|
823
|
+
"request_sha256": self.request_sha256,
|
|
824
|
+
"forecasts": [value.to_record() for value in self.forecasts],
|
|
825
|
+
"allocations": [value.to_record() for value in self.allocations],
|
|
826
|
+
"evaluations": [value.to_record() for value in self.evaluations],
|
|
827
|
+
"evaluation_reuse": self.evaluation_reuse.to_record(),
|
|
828
|
+
"phase_commit_policy": self.phase_commit_policy.to_record(),
|
|
829
|
+
"phase_receipts": [value.to_record() for value in self.phase_receipts],
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
@property
|
|
833
|
+
def receipt_sha256(self) -> str:
|
|
834
|
+
return _hash(_RESULT_DOMAIN, self._unsigned_record())
|
|
835
|
+
|
|
836
|
+
def to_record(self) -> dict[str, object]:
|
|
837
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
838
|
+
|
|
839
|
+
def __eq__(self, other: object) -> bool:
|
|
840
|
+
return (
|
|
841
|
+
type(self) is PreparedTwoStageActionEvolutionResult
|
|
842
|
+
and type(other) is PreparedTwoStageActionEvolutionResult
|
|
843
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
__hash__ = None
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
@dataclass(frozen=True, slots=True)
|
|
850
|
+
class PreparedTwoStageActionEvolution:
|
|
851
|
+
"""Execute prepared M/P/N forecasts, allocations, and policy-bound G2 evaluation."""
|
|
852
|
+
|
|
853
|
+
forecaster: ActionForecastPolicy
|
|
854
|
+
allocator: DeterministicActionAllocator
|
|
855
|
+
|
|
856
|
+
def __post_init__(self) -> None:
|
|
857
|
+
if not callable(getattr(self.forecaster, "forecast", None)):
|
|
858
|
+
raise TypeError("forecaster must expose an async forecast method")
|
|
859
|
+
if not callable(getattr(self.allocator, "allocate", None)):
|
|
860
|
+
raise TypeError("allocator must expose an allocate method")
|
|
861
|
+
|
|
862
|
+
async def run(
|
|
863
|
+
self,
|
|
864
|
+
request: PreparedTwoStageActionEvolutionRequest,
|
|
865
|
+
*,
|
|
866
|
+
phase_commit_sink: TwoStageActionPhaseCommitSink | None = None,
|
|
867
|
+
) -> PreparedTwoStageActionEvolutionResult:
|
|
868
|
+
if type(request) is not PreparedTwoStageActionEvolutionRequest:
|
|
869
|
+
raise TypeError("request must be exact PreparedTwoStageActionEvolutionRequest")
|
|
870
|
+
request.__post_init__()
|
|
871
|
+
self.__post_init__()
|
|
872
|
+
if (
|
|
873
|
+
request.phase_commit_policy.requirement
|
|
874
|
+
is DurablePhaseCommitRequirement.REQUIRED
|
|
875
|
+
and phase_commit_sink is None
|
|
876
|
+
):
|
|
877
|
+
# Fail before spending provider or evaluator compute when a
|
|
878
|
+
# prospective run cannot establish its durability boundary.
|
|
879
|
+
raise TwoStageActionPhaseCommitError(
|
|
880
|
+
"required durable phase-commit sink is absent"
|
|
881
|
+
)
|
|
882
|
+
if phase_commit_sink is not None and not callable(
|
|
883
|
+
getattr(phase_commit_sink, "commit", None)
|
|
884
|
+
):
|
|
885
|
+
raise TypeError("phase_commit_sink must expose a commit method")
|
|
886
|
+
|
|
887
|
+
raw_forecasts = await _gather_all_settled(
|
|
888
|
+
tuple(
|
|
889
|
+
self.forecaster.forecast(plan.request)
|
|
890
|
+
for plan in request.arm_plans
|
|
891
|
+
)
|
|
892
|
+
)
|
|
893
|
+
forecast_executions: list[ActionForecastArmExecution] = []
|
|
894
|
+
for plan, result in zip(request.arm_plans, raw_forecasts, strict=True):
|
|
895
|
+
if type(result) is not ActionForecastResult:
|
|
896
|
+
raise TypeError("forecaster returned a non-ActionForecastResult")
|
|
897
|
+
validate_resolved_action_forecasts(plan.request, result.forecasts)
|
|
898
|
+
forecast_executions.append(
|
|
899
|
+
ActionForecastArmExecution(
|
|
900
|
+
arm=plan.arm,
|
|
901
|
+
request_sha256=plan.request.request_sha256,
|
|
902
|
+
result=result,
|
|
903
|
+
)
|
|
904
|
+
)
|
|
905
|
+
forecasts = tuple(forecast_executions)
|
|
906
|
+
forecast_payload = _freeze_phase_payload(
|
|
907
|
+
{
|
|
908
|
+
"schema_version": 2,
|
|
909
|
+
"phase": TwoStageActionPhase.FORECAST.value,
|
|
910
|
+
"run_request_sha256": request.request_sha256,
|
|
911
|
+
"arm_executions": [
|
|
912
|
+
{
|
|
913
|
+
"arm": value.arm.value,
|
|
914
|
+
"request_sha256": value.request_sha256,
|
|
915
|
+
"resolved_action_forecast_batch": (
|
|
916
|
+
value.result.forecasts.to_record()
|
|
917
|
+
),
|
|
918
|
+
"telemetry": _agentic_call_telemetry_record(
|
|
919
|
+
value.result.telemetry
|
|
920
|
+
),
|
|
921
|
+
}
|
|
922
|
+
for value in forecasts
|
|
923
|
+
],
|
|
924
|
+
}
|
|
925
|
+
)
|
|
926
|
+
forecast_receipt = TwoStageActionPhaseReceipt(
|
|
927
|
+
phase=TwoStageActionPhase.FORECAST,
|
|
928
|
+
input_sha256=_hash(
|
|
929
|
+
b"agent-evolve:two-stage-forecast-input:v1\x00",
|
|
930
|
+
[plan.to_record() for plan in request.arm_plans],
|
|
931
|
+
),
|
|
932
|
+
output_sha256=typed_json_sha256(forecast_payload),
|
|
933
|
+
)
|
|
934
|
+
await _publish_phase_commit(
|
|
935
|
+
policy=request.phase_commit_policy,
|
|
936
|
+
sink=phase_commit_sink,
|
|
937
|
+
phase_commit=_phase_commit(
|
|
938
|
+
receipt=forecast_receipt,
|
|
939
|
+
payload=forecast_payload,
|
|
940
|
+
),
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
allocations_list: list[ActionAllocationArmExecution] = []
|
|
944
|
+
for plan, forecast in zip(request.arm_plans, forecasts, strict=True):
|
|
945
|
+
allocation_request = ActionAllocationRequest(
|
|
946
|
+
forecast_request=plan.request,
|
|
947
|
+
forecasts=forecast.result.forecasts,
|
|
948
|
+
eligible_option_ids=request.eligible_option_ids,
|
|
949
|
+
portfolio_size=request.portfolio_size,
|
|
950
|
+
utility=request.utility,
|
|
951
|
+
)
|
|
952
|
+
allocation_result = self.allocator.allocate(allocation_request)
|
|
953
|
+
if type(allocation_result) is not ActionAllocationResult:
|
|
954
|
+
raise TypeError("allocator returned a non-ActionAllocationResult")
|
|
955
|
+
allocations_list.append(
|
|
956
|
+
ActionAllocationArmExecution(
|
|
957
|
+
arm=plan.arm,
|
|
958
|
+
request=allocation_request,
|
|
959
|
+
result=allocation_result,
|
|
960
|
+
)
|
|
961
|
+
)
|
|
962
|
+
allocations = tuple(allocations_list)
|
|
963
|
+
allocation_payload = _freeze_phase_payload(
|
|
964
|
+
{
|
|
965
|
+
"schema_version": 1,
|
|
966
|
+
"phase": TwoStageActionPhase.ALLOCATE.value,
|
|
967
|
+
"run_request_sha256": request.request_sha256,
|
|
968
|
+
"arm_executions": [
|
|
969
|
+
{
|
|
970
|
+
"arm": value.arm.value,
|
|
971
|
+
"allocation_request": value.request.to_record(),
|
|
972
|
+
"decision": value.result.decision.to_record(),
|
|
973
|
+
}
|
|
974
|
+
for value in allocations
|
|
975
|
+
],
|
|
976
|
+
}
|
|
977
|
+
)
|
|
978
|
+
allocation_receipt = TwoStageActionPhaseReceipt(
|
|
979
|
+
phase=TwoStageActionPhase.ALLOCATE,
|
|
980
|
+
input_sha256=forecast_receipt.output_sha256,
|
|
981
|
+
output_sha256=typed_json_sha256(allocation_payload),
|
|
982
|
+
)
|
|
983
|
+
# This await is the oracle-firewall boundary: no evaluator coroutine is
|
|
984
|
+
# even constructed until the selected decisions are durably accepted.
|
|
985
|
+
await _publish_phase_commit(
|
|
986
|
+
policy=request.phase_commit_policy,
|
|
987
|
+
sink=phase_commit_sink,
|
|
988
|
+
phase_commit=_phase_commit(
|
|
989
|
+
receipt=allocation_receipt,
|
|
990
|
+
payload=allocation_payload,
|
|
991
|
+
),
|
|
992
|
+
)
|
|
993
|
+
|
|
994
|
+
if request.evaluation_reuse.mode is ActionEvaluationReuseMode.PER_ARM:
|
|
995
|
+
# Arm order and allocated rank are already canonical. Repeating the
|
|
996
|
+
# same option across arms intentionally consumes matched compute.
|
|
997
|
+
evaluation_requests = tuple(
|
|
998
|
+
FiniteActionEvaluationRequest(
|
|
999
|
+
run_id=request.run_id,
|
|
1000
|
+
finite_contract_identity_sha256=(
|
|
1001
|
+
request.finite_variation_contract.identity_sha256
|
|
1002
|
+
),
|
|
1003
|
+
option=request.finite_variation_contract.resolve(member.option_id),
|
|
1004
|
+
selected_by_arms=(allocation.arm,),
|
|
1005
|
+
context=request.evaluation_context,
|
|
1006
|
+
)
|
|
1007
|
+
for allocation in allocations
|
|
1008
|
+
for member in allocation.result.decision.members
|
|
1009
|
+
)
|
|
1010
|
+
else:
|
|
1011
|
+
selected_by: dict[str, list[PortfolioExperimentalArm]] = {}
|
|
1012
|
+
for allocation in allocations:
|
|
1013
|
+
for member in allocation.result.decision.members:
|
|
1014
|
+
selected_by.setdefault(member.option_id, []).append(allocation.arm)
|
|
1015
|
+
# Contract order, rather than task completion order, is the durable
|
|
1016
|
+
# ordering for explicitly reusable deterministic evaluations.
|
|
1017
|
+
evaluation_requests = tuple(
|
|
1018
|
+
FiniteActionEvaluationRequest(
|
|
1019
|
+
run_id=request.run_id,
|
|
1020
|
+
finite_contract_identity_sha256=(
|
|
1021
|
+
request.finite_variation_contract.identity_sha256
|
|
1022
|
+
),
|
|
1023
|
+
option=option,
|
|
1024
|
+
selected_by_arms=tuple(selected_by[option.option_id]),
|
|
1025
|
+
context=request.evaluation_context,
|
|
1026
|
+
)
|
|
1027
|
+
for option in request.finite_variation_contract.options
|
|
1028
|
+
if option.option_id in selected_by
|
|
1029
|
+
)
|
|
1030
|
+
raw_outcomes = await _gather_all_settled(
|
|
1031
|
+
tuple(
|
|
1032
|
+
request.evaluator.evaluator.evaluate(evaluation_request)
|
|
1033
|
+
for evaluation_request in evaluation_requests
|
|
1034
|
+
)
|
|
1035
|
+
)
|
|
1036
|
+
evaluations_list: list[FiniteActionEvaluationResult] = []
|
|
1037
|
+
for evaluation_request, outcome in zip(
|
|
1038
|
+
evaluation_requests,
|
|
1039
|
+
raw_outcomes,
|
|
1040
|
+
strict=True,
|
|
1041
|
+
):
|
|
1042
|
+
if type(outcome) is not FrozenJsonObject:
|
|
1043
|
+
raise TypeError("evaluator returned a non-FrozenJsonObject outcome")
|
|
1044
|
+
evaluations_list.append(
|
|
1045
|
+
FiniteActionEvaluationResult(
|
|
1046
|
+
request=evaluation_request,
|
|
1047
|
+
outcome=outcome,
|
|
1048
|
+
evaluator_id=request.evaluator.evaluator_id,
|
|
1049
|
+
evaluator_version=request.evaluator.evaluator_version,
|
|
1050
|
+
evaluator_definition_sha256=(
|
|
1051
|
+
request.evaluator.definition_sha256
|
|
1052
|
+
),
|
|
1053
|
+
)
|
|
1054
|
+
)
|
|
1055
|
+
evaluations = tuple(evaluations_list)
|
|
1056
|
+
evaluation_payload = _freeze_phase_payload(
|
|
1057
|
+
{
|
|
1058
|
+
"schema_version": 1,
|
|
1059
|
+
"phase": TwoStageActionPhase.EVALUATE.value,
|
|
1060
|
+
"run_request_sha256": request.request_sha256,
|
|
1061
|
+
"evaluation_results": [
|
|
1062
|
+
{
|
|
1063
|
+
**value.to_record(),
|
|
1064
|
+
"outcome": thaw_json(value.outcome),
|
|
1065
|
+
}
|
|
1066
|
+
for value in evaluations
|
|
1067
|
+
],
|
|
1068
|
+
}
|
|
1069
|
+
)
|
|
1070
|
+
evaluation_receipt = TwoStageActionPhaseReceipt(
|
|
1071
|
+
phase=TwoStageActionPhase.EVALUATE,
|
|
1072
|
+
input_sha256=_hash(
|
|
1073
|
+
b"agent-evolve:two-stage-evaluation-input:v1\x00",
|
|
1074
|
+
[value.to_record() for value in evaluation_requests],
|
|
1075
|
+
),
|
|
1076
|
+
output_sha256=typed_json_sha256(evaluation_payload),
|
|
1077
|
+
)
|
|
1078
|
+
await _publish_phase_commit(
|
|
1079
|
+
policy=request.phase_commit_policy,
|
|
1080
|
+
sink=phase_commit_sink,
|
|
1081
|
+
phase_commit=_phase_commit(
|
|
1082
|
+
receipt=evaluation_receipt,
|
|
1083
|
+
payload=evaluation_payload,
|
|
1084
|
+
),
|
|
1085
|
+
)
|
|
1086
|
+
return PreparedTwoStageActionEvolutionResult(
|
|
1087
|
+
request_sha256=request.request_sha256,
|
|
1088
|
+
forecasts=forecasts,
|
|
1089
|
+
allocations=allocations,
|
|
1090
|
+
evaluations=evaluations,
|
|
1091
|
+
evaluation_reuse=request.evaluation_reuse,
|
|
1092
|
+
phase_commit_policy=request.phase_commit_policy,
|
|
1093
|
+
phase_receipts=(
|
|
1094
|
+
forecast_receipt,
|
|
1095
|
+
allocation_receipt,
|
|
1096
|
+
evaluation_receipt,
|
|
1097
|
+
),
|
|
1098
|
+
)
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
__all__ = [
|
|
1102
|
+
"ACTION_EVALUATION_REUSE_POLICY_DEFINITION_SHA256",
|
|
1103
|
+
"ACTION_EVALUATION_REUSE_POLICY_ID",
|
|
1104
|
+
"ACTION_EVALUATION_REUSE_POLICY_VERSION",
|
|
1105
|
+
"DURABLE_PHASE_COMMIT_POLICY_DEFINITION_SHA256",
|
|
1106
|
+
"DURABLE_PHASE_COMMIT_POLICY_ID",
|
|
1107
|
+
"DURABLE_PHASE_COMMIT_POLICY_VERSION",
|
|
1108
|
+
"ActionEvaluationReuseMode",
|
|
1109
|
+
"ActionEvaluationReusePolicyBinding",
|
|
1110
|
+
"ActionAllocationArmExecution",
|
|
1111
|
+
"ActionForecastArmExecution",
|
|
1112
|
+
"ActionForecastArmPlan",
|
|
1113
|
+
"DurablePhaseCommitPolicyBinding",
|
|
1114
|
+
"DurablePhaseCommitRequirement",
|
|
1115
|
+
"FiniteActionEvaluationRequest",
|
|
1116
|
+
"FiniteActionEvaluationResult",
|
|
1117
|
+
"FiniteActionEvaluator",
|
|
1118
|
+
"FiniteActionEvaluatorBinding",
|
|
1119
|
+
"PreparedTwoStageActionEvolution",
|
|
1120
|
+
"PreparedTwoStageActionEvolutionRequest",
|
|
1121
|
+
"PreparedTwoStageActionEvolutionResult",
|
|
1122
|
+
"SCIENTIFIC_ARM_ORDER",
|
|
1123
|
+
"TwoStageActionPhase",
|
|
1124
|
+
"TwoStageActionPhaseCommit",
|
|
1125
|
+
"TwoStageActionPhaseCommitError",
|
|
1126
|
+
"TwoStageActionPhaseCommitSink",
|
|
1127
|
+
"TwoStageActionPhaseReceipt",
|
|
1128
|
+
"optional_phase_commit_policy",
|
|
1129
|
+
"per_arm_evaluation_reuse_policy",
|
|
1130
|
+
"required_scientific_phase_commit_policy",
|
|
1131
|
+
]
|