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,733 @@
|
|
|
1
|
+
"""Benchmark-neutral ports and receipts for forecast-based action allocation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import math
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Protocol, runtime_checkable
|
|
12
|
+
|
|
13
|
+
from agent_evolve.core.optimization_semantics import OptimizationSemantics
|
|
14
|
+
from agent_evolve.domain.patch import require_sha256
|
|
15
|
+
from agent_evolve.ports.action_forecast import (
|
|
16
|
+
ActionForecastRequest,
|
|
17
|
+
MetricForecastScale,
|
|
18
|
+
ParentMetricValue,
|
|
19
|
+
ResolvedActionForecast,
|
|
20
|
+
ResolvedActionForecastBatch,
|
|
21
|
+
validate_resolved_action_forecasts,
|
|
22
|
+
)
|
|
23
|
+
from agent_evolve.ports.portfolio_selection import (
|
|
24
|
+
finite_option_ids_have_pairwise_disjoint_parent_patch_subset,
|
|
25
|
+
pairwise_disjoint_parent_patch_pairs,
|
|
26
|
+
pairwise_disjoint_parent_patch_witness,
|
|
27
|
+
single_path_parent_patch_option_ids,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
32
|
+
_OPTION_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,255}$")
|
|
33
|
+
_ARM_ID = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
34
|
+
_REQUEST_DOMAIN = b"agent-evolve:action-allocation-request:v4\x00"
|
|
35
|
+
_DECISION_DOMAIN = b"agent-evolve:action-portfolio-decision:v1\x00"
|
|
36
|
+
_ELIGIBLE_DOMAIN = b"agent-evolve:eligible-action-set:v1\x00"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _canonical_json(value: object) -> bytes:
|
|
40
|
+
return json.dumps(
|
|
41
|
+
value,
|
|
42
|
+
allow_nan=False,
|
|
43
|
+
ensure_ascii=True,
|
|
44
|
+
separators=(",", ":"),
|
|
45
|
+
sort_keys=True,
|
|
46
|
+
).encode("ascii")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
50
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _finite_float(value: object, name: str) -> float:
|
|
54
|
+
if type(value) is not float or not math.isfinite(value):
|
|
55
|
+
raise TypeError(f"{name} must be a finite canonical float")
|
|
56
|
+
return value
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ForecastQuantile(str, Enum):
|
|
60
|
+
P10 = "p10"
|
|
61
|
+
P50 = "p50"
|
|
62
|
+
P90 = "p90"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True, slots=True)
|
|
66
|
+
class ForecastPortfolioUtilityInput:
|
|
67
|
+
"""One benchmark utility query over a candidate forecast portfolio."""
|
|
68
|
+
|
|
69
|
+
optimization_semantics: OptimizationSemantics
|
|
70
|
+
parent_metric_values: tuple[ParentMetricValue, ...]
|
|
71
|
+
metric_scales: tuple[MetricForecastScale, ...]
|
|
72
|
+
members: tuple[ResolvedActionForecast, ...]
|
|
73
|
+
quantile: ForecastQuantile
|
|
74
|
+
|
|
75
|
+
def __post_init__(self) -> None:
|
|
76
|
+
if type(self.optimization_semantics) is not OptimizationSemantics:
|
|
77
|
+
raise TypeError("optimization_semantics must be exact")
|
|
78
|
+
OptimizationSemantics.__post_init__(self.optimization_semantics)
|
|
79
|
+
if (
|
|
80
|
+
type(self.parent_metric_values) is not tuple
|
|
81
|
+
or not self.parent_metric_values
|
|
82
|
+
):
|
|
83
|
+
raise ValueError("parent_metric_values must be a non-empty exact tuple")
|
|
84
|
+
if any(
|
|
85
|
+
type(value) is not ParentMetricValue for value in self.parent_metric_values
|
|
86
|
+
):
|
|
87
|
+
raise TypeError("parent_metric_values must contain exact values")
|
|
88
|
+
if type(self.metric_scales) is not tuple or not self.metric_scales:
|
|
89
|
+
raise ValueError("metric_scales must be a non-empty exact tuple")
|
|
90
|
+
if any(type(value) is not MetricForecastScale for value in self.metric_scales):
|
|
91
|
+
raise TypeError("metric_scales must contain exact values")
|
|
92
|
+
if (
|
|
93
|
+
type(self.members) is not tuple
|
|
94
|
+
or not self.members
|
|
95
|
+
or any(type(value) is not ResolvedActionForecast for value in self.members)
|
|
96
|
+
):
|
|
97
|
+
raise ValueError("members must be a non-empty exact resolved tuple")
|
|
98
|
+
for value in self.members:
|
|
99
|
+
value.__post_init__()
|
|
100
|
+
if len({value.option_id for value in self.members}) != len(self.members):
|
|
101
|
+
raise ValueError("utility input cannot repeat an action")
|
|
102
|
+
if type(self.quantile) is not ForecastQuantile:
|
|
103
|
+
raise TypeError("quantile must be an exact ForecastQuantile")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@runtime_checkable
|
|
107
|
+
class ForecastPortfolioUtility(Protocol):
|
|
108
|
+
"""Deterministic benchmark policy; returned utility is higher-is-better."""
|
|
109
|
+
|
|
110
|
+
def __call__(self, request: ForecastPortfolioUtilityInput) -> float: ...
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass(frozen=True, slots=True)
|
|
114
|
+
class ForecastPortfolioUtilityBinding:
|
|
115
|
+
"""Identified executable utility injected by one benchmark adapter."""
|
|
116
|
+
|
|
117
|
+
utility: ForecastPortfolioUtility = field(repr=False, compare=False)
|
|
118
|
+
policy_id: str
|
|
119
|
+
policy_version: int
|
|
120
|
+
definition_sha256: str
|
|
121
|
+
|
|
122
|
+
def __post_init__(self) -> None:
|
|
123
|
+
if not callable(self.utility):
|
|
124
|
+
raise TypeError("utility must be callable")
|
|
125
|
+
if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
|
|
126
|
+
raise ValueError("policy_id must use the closed lowercase token grammar")
|
|
127
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
128
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
129
|
+
require_sha256(self.definition_sha256, "definition_sha256")
|
|
130
|
+
|
|
131
|
+
def to_record(self) -> dict[str, object]:
|
|
132
|
+
self.__post_init__()
|
|
133
|
+
return {
|
|
134
|
+
"policy_id": self.policy_id,
|
|
135
|
+
"policy_version": self.policy_version,
|
|
136
|
+
"definition_sha256": self.definition_sha256,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dataclass(frozen=True, slots=True)
|
|
141
|
+
class ExactActionArmCountConstraint:
|
|
142
|
+
"""One objective-blind exact exposure constraint over sealed actions.
|
|
143
|
+
|
|
144
|
+
``constraint_id`` names a generic attribution axis such as proposal source
|
|
145
|
+
or variation operator. The allocator sees only option-to-arm labels and
|
|
146
|
+
exact counts; workload, model, provider, and objective identifiers remain
|
|
147
|
+
outside this port.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
constraint_id: str
|
|
151
|
+
option_arm_ids: tuple[tuple[str, str], ...]
|
|
152
|
+
target_counts: tuple[tuple[str, int], ...]
|
|
153
|
+
|
|
154
|
+
def __post_init__(self) -> None:
|
|
155
|
+
if (
|
|
156
|
+
type(self.constraint_id) is not str
|
|
157
|
+
or _TOKEN.fullmatch(self.constraint_id) is None
|
|
158
|
+
):
|
|
159
|
+
raise ValueError("constraint_id must use the closed token grammar")
|
|
160
|
+
if type(self.option_arm_ids) is not tuple or not self.option_arm_ids:
|
|
161
|
+
raise ValueError("option_arm_ids must be a non-empty exact tuple")
|
|
162
|
+
for option_id, arm_id in self.option_arm_ids:
|
|
163
|
+
if (
|
|
164
|
+
type(option_id) is not str
|
|
165
|
+
or _OPTION_ID.fullmatch(option_id) is None
|
|
166
|
+
or type(arm_id) is not str
|
|
167
|
+
or _ARM_ID.fullmatch(arm_id) is None
|
|
168
|
+
):
|
|
169
|
+
raise ValueError("option_arm_ids contains an invalid token")
|
|
170
|
+
if self.option_arm_ids != tuple(sorted(self.option_arm_ids)) or len(
|
|
171
|
+
{value[0] for value in self.option_arm_ids}
|
|
172
|
+
) != len(self.option_arm_ids):
|
|
173
|
+
raise ValueError("option_arm_ids must be option-unique and canonical")
|
|
174
|
+
if type(self.target_counts) is not tuple or not self.target_counts:
|
|
175
|
+
raise ValueError("target_counts must be a non-empty exact tuple")
|
|
176
|
+
for arm_id, count in self.target_counts:
|
|
177
|
+
if type(arm_id) is not str or _ARM_ID.fullmatch(arm_id) is None:
|
|
178
|
+
raise ValueError("target_counts contains an invalid arm token")
|
|
179
|
+
if type(count) is not int or count < 0:
|
|
180
|
+
raise ValueError("target_counts must be non-negative exact integers")
|
|
181
|
+
if self.target_counts != tuple(sorted(self.target_counts)) or len(
|
|
182
|
+
{value[0] for value in self.target_counts}
|
|
183
|
+
) != len(self.target_counts):
|
|
184
|
+
raise ValueError("target_counts must be arm-unique and canonical")
|
|
185
|
+
if sum(value[1] for value in self.target_counts) <= 0:
|
|
186
|
+
raise ValueError("target_counts must allocate positive capacity")
|
|
187
|
+
attributed_arms = {value[1] for value in self.option_arm_ids}
|
|
188
|
+
target = dict(self.target_counts)
|
|
189
|
+
if not attributed_arms.issubset(target) or any(
|
|
190
|
+
count > 0 and arm_id not in attributed_arms
|
|
191
|
+
for arm_id, count in target.items()
|
|
192
|
+
):
|
|
193
|
+
raise ValueError(
|
|
194
|
+
"option attribution cannot realize the positive target arms"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def to_record(self) -> dict[str, object]:
|
|
198
|
+
self.__post_init__()
|
|
199
|
+
return {
|
|
200
|
+
"constraint_id": self.constraint_id,
|
|
201
|
+
"option_arm_ids": [list(value) for value in self.option_arm_ids],
|
|
202
|
+
"target_counts": [list(value) for value in self.target_counts],
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass(frozen=True, slots=True)
|
|
207
|
+
class ActionAllocationRequest:
|
|
208
|
+
"""Allocation input with a cache-safe canonical eligible-action subset."""
|
|
209
|
+
|
|
210
|
+
forecast_request: ActionForecastRequest
|
|
211
|
+
forecasts: ResolvedActionForecastBatch
|
|
212
|
+
eligible_option_ids: tuple[str, ...]
|
|
213
|
+
portfolio_size: int
|
|
214
|
+
utility: ForecastPortfolioUtilityBinding
|
|
215
|
+
min_distinct_families: int | None = None
|
|
216
|
+
require_pairwise_disjoint_parent_patches: bool = False
|
|
217
|
+
minimum_single_path_interventions: int = 0
|
|
218
|
+
minimum_disjoint_parent_patch_pairs: int = 0
|
|
219
|
+
required_option_ids: tuple[str, ...] = ()
|
|
220
|
+
exact_arm_count_constraints: tuple[ExactActionArmCountConstraint, ...] = ()
|
|
221
|
+
|
|
222
|
+
def __post_init__(self) -> None:
|
|
223
|
+
if type(self.forecast_request) is not ActionForecastRequest:
|
|
224
|
+
raise TypeError("forecast_request must be exact ActionForecastRequest")
|
|
225
|
+
self.forecast_request.__post_init__()
|
|
226
|
+
if type(self.forecasts) is not ResolvedActionForecastBatch:
|
|
227
|
+
raise TypeError("forecasts must be exact ResolvedActionForecastBatch")
|
|
228
|
+
validate_resolved_action_forecasts(self.forecast_request, self.forecasts)
|
|
229
|
+
if type(self.eligible_option_ids) is not tuple or any(
|
|
230
|
+
type(value) is not str or _OPTION_ID.fullmatch(value) is None
|
|
231
|
+
for value in self.eligible_option_ids
|
|
232
|
+
):
|
|
233
|
+
raise TypeError("eligible_option_ids must be an exact option-ID tuple")
|
|
234
|
+
if not self.eligible_option_ids:
|
|
235
|
+
raise ValueError("eligible_option_ids must be non-empty")
|
|
236
|
+
if self.eligible_option_ids != tuple(sorted(set(self.eligible_option_ids))):
|
|
237
|
+
raise ValueError("eligible_option_ids must be unique and canonical")
|
|
238
|
+
available = {value.option_id for value in self.forecasts.forecasts}
|
|
239
|
+
if not set(self.eligible_option_ids).issubset(available):
|
|
240
|
+
raise ValueError("eligible_option_ids contains a foreign option")
|
|
241
|
+
if type(self.portfolio_size) is not int or self.portfolio_size <= 0:
|
|
242
|
+
raise ValueError("portfolio_size must be a positive exact integer")
|
|
243
|
+
if self.portfolio_size > len(self.eligible_option_ids):
|
|
244
|
+
raise ValueError("portfolio_size exceeds the eligible action count")
|
|
245
|
+
if type(self.utility) is not ForecastPortfolioUtilityBinding:
|
|
246
|
+
raise TypeError("utility must be an exact identified binding")
|
|
247
|
+
self.utility.__post_init__()
|
|
248
|
+
by_id = {
|
|
249
|
+
value.option_id: value
|
|
250
|
+
for value in self.forecast_request.finite_variation_contract.options
|
|
251
|
+
}
|
|
252
|
+
available_families = {
|
|
253
|
+
by_id[option_id].family for option_id in self.eligible_option_ids
|
|
254
|
+
}
|
|
255
|
+
if self.min_distinct_families is not None:
|
|
256
|
+
if (
|
|
257
|
+
type(self.min_distinct_families) is not int
|
|
258
|
+
or self.min_distinct_families <= 0
|
|
259
|
+
):
|
|
260
|
+
raise ValueError("min_distinct_families must be positive or None")
|
|
261
|
+
if self.min_distinct_families > self.portfolio_size:
|
|
262
|
+
raise ValueError("min_distinct_families cannot exceed portfolio_size")
|
|
263
|
+
if self.min_distinct_families > len(available_families):
|
|
264
|
+
raise ValueError(
|
|
265
|
+
"eligible actions cannot satisfy min_distinct_families"
|
|
266
|
+
)
|
|
267
|
+
if type(self.require_pairwise_disjoint_parent_patches) is not bool:
|
|
268
|
+
raise TypeError(
|
|
269
|
+
"require_pairwise_disjoint_parent_patches must be exact bool"
|
|
270
|
+
)
|
|
271
|
+
if (
|
|
272
|
+
type(self.minimum_single_path_interventions) is not int
|
|
273
|
+
or not 0
|
|
274
|
+
<= self.minimum_single_path_interventions
|
|
275
|
+
<= self.portfolio_size
|
|
276
|
+
):
|
|
277
|
+
raise ValueError(
|
|
278
|
+
"minimum_single_path_interventions must lie in portfolio capacity"
|
|
279
|
+
)
|
|
280
|
+
maximum_pairs = self.portfolio_size * (self.portfolio_size - 1) // 2
|
|
281
|
+
if (
|
|
282
|
+
type(self.minimum_disjoint_parent_patch_pairs) is not int
|
|
283
|
+
or not 0
|
|
284
|
+
<= self.minimum_disjoint_parent_patch_pairs
|
|
285
|
+
<= maximum_pairs
|
|
286
|
+
):
|
|
287
|
+
raise ValueError(
|
|
288
|
+
"minimum_disjoint_parent_patch_pairs must lie in portfolio pair "
|
|
289
|
+
"capacity"
|
|
290
|
+
)
|
|
291
|
+
contract = self.forecast_request.finite_variation_contract
|
|
292
|
+
if self.minimum_single_path_interventions > len(
|
|
293
|
+
single_path_parent_patch_option_ids(contract, self.eligible_option_ids)
|
|
294
|
+
):
|
|
295
|
+
raise ValueError(
|
|
296
|
+
"eligible actions cannot satisfy the single-path intervention floor"
|
|
297
|
+
)
|
|
298
|
+
if self.minimum_disjoint_parent_patch_pairs > len(
|
|
299
|
+
pairwise_disjoint_parent_patch_pairs(contract, self.eligible_option_ids)
|
|
300
|
+
):
|
|
301
|
+
raise ValueError(
|
|
302
|
+
"eligible actions cannot satisfy the disjoint parent-pair floor"
|
|
303
|
+
)
|
|
304
|
+
if type(self.required_option_ids) is not tuple or any(
|
|
305
|
+
type(value) is not str or _OPTION_ID.fullmatch(value) is None
|
|
306
|
+
for value in self.required_option_ids
|
|
307
|
+
):
|
|
308
|
+
raise TypeError("required_option_ids must be an exact option-ID tuple")
|
|
309
|
+
if self.required_option_ids != tuple(sorted(set(self.required_option_ids))):
|
|
310
|
+
raise ValueError("required_option_ids must be unique and canonical")
|
|
311
|
+
if not set(self.required_option_ids).issubset(self.eligible_option_ids):
|
|
312
|
+
raise ValueError("required_option_ids contains an ineligible option")
|
|
313
|
+
if len(self.required_option_ids) > self.portfolio_size:
|
|
314
|
+
raise ValueError("required_option_ids exceeds the portfolio size")
|
|
315
|
+
if type(self.exact_arm_count_constraints) is not tuple or any(
|
|
316
|
+
type(value) is not ExactActionArmCountConstraint
|
|
317
|
+
for value in self.exact_arm_count_constraints
|
|
318
|
+
):
|
|
319
|
+
raise TypeError(
|
|
320
|
+
"exact_arm_count_constraints must be an exact constraint tuple"
|
|
321
|
+
)
|
|
322
|
+
for constraint in self.exact_arm_count_constraints:
|
|
323
|
+
constraint.__post_init__()
|
|
324
|
+
constraint_ids = tuple(
|
|
325
|
+
value.constraint_id for value in self.exact_arm_count_constraints
|
|
326
|
+
)
|
|
327
|
+
if constraint_ids != tuple(sorted(set(constraint_ids))):
|
|
328
|
+
raise ValueError(
|
|
329
|
+
"exact_arm_count_constraints must use unique canonical IDs"
|
|
330
|
+
)
|
|
331
|
+
for constraint in self.exact_arm_count_constraints:
|
|
332
|
+
if sum(value[1] for value in constraint.target_counts) != (
|
|
333
|
+
self.portfolio_size
|
|
334
|
+
):
|
|
335
|
+
raise ValueError(
|
|
336
|
+
"exact arm targets must allocate the complete portfolio"
|
|
337
|
+
)
|
|
338
|
+
arm_by_option = dict(constraint.option_arm_ids)
|
|
339
|
+
if set(arm_by_option) != available:
|
|
340
|
+
raise ValueError(
|
|
341
|
+
"exact arm attribution must cover the complete forecast batch"
|
|
342
|
+
)
|
|
343
|
+
target = dict(constraint.target_counts)
|
|
344
|
+
eligible_counts = {arm_id: 0 for arm_id in target}
|
|
345
|
+
required_counts = {arm_id: 0 for arm_id in target}
|
|
346
|
+
for option_id in self.eligible_option_ids:
|
|
347
|
+
eligible_counts[arm_by_option[option_id]] += 1
|
|
348
|
+
for option_id in self.required_option_ids:
|
|
349
|
+
required_counts[arm_by_option[option_id]] += 1
|
|
350
|
+
if any(eligible_counts[arm_id] < count for arm_id, count in target.items()):
|
|
351
|
+
raise ValueError("eligible actions cannot satisfy exact arm targets")
|
|
352
|
+
if any(required_counts[arm_id] > count for arm_id, count in target.items()):
|
|
353
|
+
raise ValueError("required actions exceed an exact arm target")
|
|
354
|
+
if self.require_pairwise_disjoint_parent_patches and not (
|
|
355
|
+
finite_option_ids_have_pairwise_disjoint_parent_patch_subset(
|
|
356
|
+
self.forecast_request.finite_variation_contract,
|
|
357
|
+
self.eligible_option_ids,
|
|
358
|
+
portfolio_size=self.portfolio_size,
|
|
359
|
+
min_distinct_families=self.min_distinct_families,
|
|
360
|
+
)
|
|
361
|
+
):
|
|
362
|
+
raise ValueError(
|
|
363
|
+
"eligible actions contain no structurally feasible portfolio"
|
|
364
|
+
)
|
|
365
|
+
if self.require_pairwise_disjoint_parent_patches and self.required_option_ids:
|
|
366
|
+
witness = pairwise_disjoint_parent_patch_witness(
|
|
367
|
+
self.forecast_request.finite_variation_contract,
|
|
368
|
+
self.eligible_option_ids,
|
|
369
|
+
portfolio_size=self.portfolio_size,
|
|
370
|
+
min_distinct_families=self.min_distinct_families,
|
|
371
|
+
required_option_ids=self.required_option_ids,
|
|
372
|
+
)
|
|
373
|
+
if witness is None:
|
|
374
|
+
raise ValueError(
|
|
375
|
+
"required_option_ids have no structurally feasible completion"
|
|
376
|
+
)
|
|
377
|
+
elif self.min_distinct_families is not None and self.required_option_ids:
|
|
378
|
+
required_families = {
|
|
379
|
+
by_id[value].family for value in self.required_option_ids
|
|
380
|
+
}
|
|
381
|
+
remaining_families = {
|
|
382
|
+
by_id[value].family
|
|
383
|
+
for value in self.eligible_option_ids
|
|
384
|
+
if value not in self.required_option_ids
|
|
385
|
+
}
|
|
386
|
+
slots = self.portfolio_size - len(self.required_option_ids)
|
|
387
|
+
reachable = len(required_families) + min(
|
|
388
|
+
slots,
|
|
389
|
+
len(remaining_families - required_families),
|
|
390
|
+
)
|
|
391
|
+
if reachable < self.min_distinct_families:
|
|
392
|
+
raise ValueError(
|
|
393
|
+
"required_option_ids cannot complete minimum family coverage"
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
@property
|
|
397
|
+
def eligible_options_sha256(self) -> str:
|
|
398
|
+
self.__post_init__()
|
|
399
|
+
return _hash(
|
|
400
|
+
_ELIGIBLE_DOMAIN,
|
|
401
|
+
{"eligible_option_ids": list(self.eligible_option_ids)},
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
def to_record(self) -> dict[str, object]:
|
|
405
|
+
self.__post_init__()
|
|
406
|
+
structural_floor_bound = bool(
|
|
407
|
+
self.minimum_single_path_interventions
|
|
408
|
+
or self.minimum_disjoint_parent_patch_pairs
|
|
409
|
+
)
|
|
410
|
+
record: dict[str, object] = {
|
|
411
|
+
"schema_version": 5 if structural_floor_bound else 4,
|
|
412
|
+
"forecast_request_sha256": self.forecast_request.request_sha256,
|
|
413
|
+
"forecast_receipt_sha256": self.forecasts.receipt_sha256,
|
|
414
|
+
"eligible_option_ids": list(self.eligible_option_ids),
|
|
415
|
+
"eligible_options_sha256": self.eligible_options_sha256,
|
|
416
|
+
"portfolio_size": self.portfolio_size,
|
|
417
|
+
"utility": self.utility.to_record(),
|
|
418
|
+
"min_distinct_families": self.min_distinct_families,
|
|
419
|
+
"require_pairwise_disjoint_parent_patches": (
|
|
420
|
+
self.require_pairwise_disjoint_parent_patches
|
|
421
|
+
),
|
|
422
|
+
"required_option_ids": list(self.required_option_ids),
|
|
423
|
+
"exact_arm_count_constraints": [
|
|
424
|
+
value.to_record() for value in self.exact_arm_count_constraints
|
|
425
|
+
],
|
|
426
|
+
}
|
|
427
|
+
if self.minimum_single_path_interventions:
|
|
428
|
+
record["minimum_single_path_interventions"] = (
|
|
429
|
+
self.minimum_single_path_interventions
|
|
430
|
+
)
|
|
431
|
+
if self.minimum_disjoint_parent_patch_pairs:
|
|
432
|
+
record["minimum_disjoint_parent_patch_pairs"] = (
|
|
433
|
+
self.minimum_disjoint_parent_patch_pairs
|
|
434
|
+
)
|
|
435
|
+
return record
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
def request_sha256(self) -> str:
|
|
439
|
+
return _hash(_REQUEST_DOMAIN, self.to_record())
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
@dataclass(frozen=True, slots=True)
|
|
443
|
+
class PortfolioAllocationScore:
|
|
444
|
+
"""Auditable generic score for one greedy marginal candidate."""
|
|
445
|
+
|
|
446
|
+
p10_utility: float
|
|
447
|
+
p50_utility: float
|
|
448
|
+
p90_utility: float
|
|
449
|
+
downside_utility: float
|
|
450
|
+
risk_penalty: float
|
|
451
|
+
diversity_reward: float
|
|
452
|
+
total_utility: float
|
|
453
|
+
|
|
454
|
+
def __post_init__(self) -> None:
|
|
455
|
+
for name in (
|
|
456
|
+
"p10_utility",
|
|
457
|
+
"p50_utility",
|
|
458
|
+
"p90_utility",
|
|
459
|
+
"downside_utility",
|
|
460
|
+
"risk_penalty",
|
|
461
|
+
"diversity_reward",
|
|
462
|
+
"total_utility",
|
|
463
|
+
):
|
|
464
|
+
_finite_float(getattr(self, name), name)
|
|
465
|
+
if self.downside_utility != min(self.p10_utility, self.p90_utility):
|
|
466
|
+
raise ValueError("downside_utility must be min(p10_utility,p90_utility)")
|
|
467
|
+
if self.risk_penalty < 0.0:
|
|
468
|
+
raise ValueError("risk_penalty must be non-negative")
|
|
469
|
+
if self.diversity_reward < 0.0:
|
|
470
|
+
raise ValueError("diversity_reward must be non-negative")
|
|
471
|
+
|
|
472
|
+
def to_record(self) -> dict[str, object]:
|
|
473
|
+
self.__post_init__()
|
|
474
|
+
return {
|
|
475
|
+
name: getattr(self, name).hex()
|
|
476
|
+
for name in (
|
|
477
|
+
"p10_utility",
|
|
478
|
+
"p50_utility",
|
|
479
|
+
"p90_utility",
|
|
480
|
+
"downside_utility",
|
|
481
|
+
"risk_penalty",
|
|
482
|
+
"diversity_reward",
|
|
483
|
+
"total_utility",
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
@dataclass(frozen=True, slots=True)
|
|
489
|
+
class AllocatedActionMember:
|
|
490
|
+
rank: int
|
|
491
|
+
option_id: str
|
|
492
|
+
option_identity_sha256: str
|
|
493
|
+
child_configuration_sha256: str
|
|
494
|
+
family: str
|
|
495
|
+
greedy_step_score: PortfolioAllocationScore
|
|
496
|
+
marginal_total_utility: float
|
|
497
|
+
|
|
498
|
+
def __post_init__(self) -> None:
|
|
499
|
+
if type(self.rank) is not int or self.rank <= 0:
|
|
500
|
+
raise ValueError("rank must be a positive exact integer")
|
|
501
|
+
if (
|
|
502
|
+
type(self.option_id) is not str
|
|
503
|
+
or _OPTION_ID.fullmatch(self.option_id) is None
|
|
504
|
+
):
|
|
505
|
+
raise ValueError("option_id must use the finite-option grammar")
|
|
506
|
+
if type(self.family) is not str or _TOKEN.fullmatch(self.family) is None:
|
|
507
|
+
raise ValueError("family must use the closed token grammar")
|
|
508
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
509
|
+
require_sha256(
|
|
510
|
+
self.child_configuration_sha256,
|
|
511
|
+
"child_configuration_sha256",
|
|
512
|
+
)
|
|
513
|
+
if type(self.greedy_step_score) is not PortfolioAllocationScore:
|
|
514
|
+
raise TypeError("greedy_step_score must be exact PortfolioAllocationScore")
|
|
515
|
+
self.greedy_step_score.__post_init__()
|
|
516
|
+
_finite_float(self.marginal_total_utility, "marginal_total_utility")
|
|
517
|
+
|
|
518
|
+
def to_record(self) -> dict[str, object]:
|
|
519
|
+
self.__post_init__()
|
|
520
|
+
return {
|
|
521
|
+
"rank": self.rank,
|
|
522
|
+
"option_id": self.option_id,
|
|
523
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
524
|
+
"child_configuration_sha256": self.child_configuration_sha256,
|
|
525
|
+
"family": self.family,
|
|
526
|
+
"greedy_step_score": self.greedy_step_score.to_record(),
|
|
527
|
+
"marginal_total_utility_hex": self.marginal_total_utility.hex(),
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
532
|
+
class ActionPortfolioDecision:
|
|
533
|
+
"""Receipt-bound deterministic k-option allocation decision."""
|
|
534
|
+
|
|
535
|
+
allocation_request_sha256: str
|
|
536
|
+
forecast_receipt_sha256: str
|
|
537
|
+
finite_contract_identity_sha256: str
|
|
538
|
+
eligible_options_sha256: str
|
|
539
|
+
members: tuple[AllocatedActionMember, ...]
|
|
540
|
+
final_score: PortfolioAllocationScore
|
|
541
|
+
candidate_evaluations: int
|
|
542
|
+
utility_policy_id: str
|
|
543
|
+
utility_policy_version: int
|
|
544
|
+
utility_definition_sha256: str
|
|
545
|
+
allocator_policy_id: str
|
|
546
|
+
allocator_policy_version: int
|
|
547
|
+
allocator_definition_sha256: str
|
|
548
|
+
allocator_configuration_sha256: str
|
|
549
|
+
|
|
550
|
+
def __post_init__(self) -> None:
|
|
551
|
+
for name in (
|
|
552
|
+
"allocation_request_sha256",
|
|
553
|
+
"forecast_receipt_sha256",
|
|
554
|
+
"finite_contract_identity_sha256",
|
|
555
|
+
"eligible_options_sha256",
|
|
556
|
+
"utility_definition_sha256",
|
|
557
|
+
"allocator_definition_sha256",
|
|
558
|
+
"allocator_configuration_sha256",
|
|
559
|
+
):
|
|
560
|
+
require_sha256(getattr(self, name), name)
|
|
561
|
+
if (
|
|
562
|
+
type(self.members) is not tuple
|
|
563
|
+
or not self.members
|
|
564
|
+
or any(type(value) is not AllocatedActionMember for value in self.members)
|
|
565
|
+
):
|
|
566
|
+
raise ValueError("members must be a non-empty exact allocated tuple")
|
|
567
|
+
for value in self.members:
|
|
568
|
+
value.__post_init__()
|
|
569
|
+
if tuple(value.rank for value in self.members) != tuple(
|
|
570
|
+
range(1, len(self.members) + 1)
|
|
571
|
+
):
|
|
572
|
+
raise ValueError("member ranks must be contiguous and ordered")
|
|
573
|
+
if len({value.option_id for value in self.members}) != len(self.members):
|
|
574
|
+
raise ValueError("allocation decision cannot repeat an option")
|
|
575
|
+
if type(self.final_score) is not PortfolioAllocationScore:
|
|
576
|
+
raise TypeError("final_score must be exact PortfolioAllocationScore")
|
|
577
|
+
self.final_score.__post_init__()
|
|
578
|
+
if self.final_score != self.members[-1].greedy_step_score:
|
|
579
|
+
raise ValueError("final_score must equal the last greedy step score")
|
|
580
|
+
if (
|
|
581
|
+
type(self.candidate_evaluations) is not int
|
|
582
|
+
or self.candidate_evaluations <= 0
|
|
583
|
+
):
|
|
584
|
+
raise ValueError("candidate_evaluations must be a positive exact integer")
|
|
585
|
+
for prefix in ("utility", "allocator"):
|
|
586
|
+
policy_id = getattr(self, f"{prefix}_policy_id")
|
|
587
|
+
policy_version = getattr(self, f"{prefix}_policy_version")
|
|
588
|
+
if type(policy_id) is not str or _TOKEN.fullmatch(policy_id) is None:
|
|
589
|
+
raise ValueError(f"{prefix}_policy_id must use the token grammar")
|
|
590
|
+
if type(policy_version) is not int or policy_version <= 0:
|
|
591
|
+
raise ValueError(f"{prefix}_policy_version must be positive")
|
|
592
|
+
|
|
593
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
594
|
+
self.__post_init__()
|
|
595
|
+
return {
|
|
596
|
+
"schema_version": 1,
|
|
597
|
+
"allocation_request_sha256": self.allocation_request_sha256,
|
|
598
|
+
"forecast_receipt_sha256": self.forecast_receipt_sha256,
|
|
599
|
+
"finite_contract_identity_sha256": self.finite_contract_identity_sha256,
|
|
600
|
+
"eligible_options_sha256": self.eligible_options_sha256,
|
|
601
|
+
"members": [value.to_record() for value in self.members],
|
|
602
|
+
"final_score": self.final_score.to_record(),
|
|
603
|
+
"candidate_evaluations": self.candidate_evaluations,
|
|
604
|
+
"utility_policy": {
|
|
605
|
+
"policy_id": self.utility_policy_id,
|
|
606
|
+
"policy_version": self.utility_policy_version,
|
|
607
|
+
"definition_sha256": self.utility_definition_sha256,
|
|
608
|
+
},
|
|
609
|
+
"allocator_policy": {
|
|
610
|
+
"policy_id": self.allocator_policy_id,
|
|
611
|
+
"policy_version": self.allocator_policy_version,
|
|
612
|
+
"definition_sha256": self.allocator_definition_sha256,
|
|
613
|
+
"configuration_sha256": self.allocator_configuration_sha256,
|
|
614
|
+
},
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
@property
|
|
618
|
+
def receipt_sha256(self) -> str:
|
|
619
|
+
return _hash(_DECISION_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
|
+
def __eq__(self, other: object) -> bool:
|
|
625
|
+
return (
|
|
626
|
+
type(self) is ActionPortfolioDecision
|
|
627
|
+
and type(other) is ActionPortfolioDecision
|
|
628
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
__hash__ = None
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
@dataclass(frozen=True, slots=True)
|
|
635
|
+
class ActionAllocationResult:
|
|
636
|
+
decision: ActionPortfolioDecision
|
|
637
|
+
|
|
638
|
+
def __post_init__(self) -> None:
|
|
639
|
+
if type(self.decision) is not ActionPortfolioDecision:
|
|
640
|
+
raise TypeError("decision must be exact ActionPortfolioDecision")
|
|
641
|
+
self.decision.__post_init__()
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
@runtime_checkable
|
|
645
|
+
class DeterministicActionAllocator(Protocol):
|
|
646
|
+
def allocate(self, request: ActionAllocationRequest) -> ActionAllocationResult: ...
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def validate_action_portfolio_decision(
|
|
650
|
+
request: ActionAllocationRequest,
|
|
651
|
+
decision: ActionPortfolioDecision,
|
|
652
|
+
) -> None:
|
|
653
|
+
"""Validate receipt bindings and selected action identities without rerunning utility."""
|
|
654
|
+
|
|
655
|
+
if type(request) is not ActionAllocationRequest:
|
|
656
|
+
raise TypeError("request must be exact ActionAllocationRequest")
|
|
657
|
+
request.__post_init__()
|
|
658
|
+
if type(decision) is not ActionPortfolioDecision:
|
|
659
|
+
raise TypeError("decision must be exact ActionPortfolioDecision")
|
|
660
|
+
decision.__post_init__()
|
|
661
|
+
if (
|
|
662
|
+
decision.allocation_request_sha256 != request.request_sha256
|
|
663
|
+
or decision.forecast_receipt_sha256 != request.forecasts.receipt_sha256
|
|
664
|
+
or decision.finite_contract_identity_sha256
|
|
665
|
+
!= request.forecast_request.finite_variation_contract.identity_sha256
|
|
666
|
+
or decision.eligible_options_sha256 != request.eligible_options_sha256
|
|
667
|
+
):
|
|
668
|
+
raise ValueError("allocation decision is bound to a different request")
|
|
669
|
+
if len(decision.members) != request.portfolio_size:
|
|
670
|
+
raise ValueError("allocation member count differs from portfolio_size")
|
|
671
|
+
if (
|
|
672
|
+
decision.utility_policy_id != request.utility.policy_id
|
|
673
|
+
or decision.utility_policy_version != request.utility.policy_version
|
|
674
|
+
or decision.utility_definition_sha256 != request.utility.definition_sha256
|
|
675
|
+
):
|
|
676
|
+
raise ValueError("allocation decision names a different utility policy")
|
|
677
|
+
forecasts = {value.option_id: value for value in request.forecasts.forecasts}
|
|
678
|
+
for member in decision.members:
|
|
679
|
+
if member.option_id not in request.eligible_option_ids:
|
|
680
|
+
raise ValueError("allocation selected an ineligible option")
|
|
681
|
+
forecast = forecasts[member.option_id]
|
|
682
|
+
if (
|
|
683
|
+
member.option_identity_sha256 != forecast.option_identity_sha256
|
|
684
|
+
or member.child_configuration_sha256 != forecast.child_configuration_sha256
|
|
685
|
+
or member.family != forecast.family
|
|
686
|
+
):
|
|
687
|
+
raise ValueError("allocation member differs from its resolved forecast")
|
|
688
|
+
if not set(request.required_option_ids).issubset(
|
|
689
|
+
member.option_id for member in decision.members
|
|
690
|
+
):
|
|
691
|
+
raise ValueError("allocation decision omitted a required option")
|
|
692
|
+
selected_ids = tuple(member.option_id for member in decision.members)
|
|
693
|
+
if len(
|
|
694
|
+
single_path_parent_patch_option_ids(
|
|
695
|
+
request.forecast_request.finite_variation_contract,
|
|
696
|
+
selected_ids,
|
|
697
|
+
)
|
|
698
|
+
) < request.minimum_single_path_interventions:
|
|
699
|
+
raise ValueError(
|
|
700
|
+
"allocation decision violated its single-path intervention floor"
|
|
701
|
+
)
|
|
702
|
+
if len(
|
|
703
|
+
pairwise_disjoint_parent_patch_pairs(
|
|
704
|
+
request.forecast_request.finite_variation_contract,
|
|
705
|
+
selected_ids,
|
|
706
|
+
)
|
|
707
|
+
) < request.minimum_disjoint_parent_patch_pairs:
|
|
708
|
+
raise ValueError(
|
|
709
|
+
"allocation decision violated its disjoint parent-pair floor"
|
|
710
|
+
)
|
|
711
|
+
for constraint in request.exact_arm_count_constraints:
|
|
712
|
+
arm_by_option = dict(constraint.option_arm_ids)
|
|
713
|
+
realized = {arm_id: 0 for arm_id, _ in constraint.target_counts}
|
|
714
|
+
for option_id in selected_ids:
|
|
715
|
+
realized[arm_by_option[option_id]] += 1
|
|
716
|
+
if tuple(sorted(realized.items())) != constraint.target_counts:
|
|
717
|
+
raise ValueError("allocation decision violated an exact arm target")
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
__all__ = [
|
|
721
|
+
"ActionAllocationRequest",
|
|
722
|
+
"ActionAllocationResult",
|
|
723
|
+
"ActionPortfolioDecision",
|
|
724
|
+
"AllocatedActionMember",
|
|
725
|
+
"ExactActionArmCountConstraint",
|
|
726
|
+
"DeterministicActionAllocator",
|
|
727
|
+
"ForecastPortfolioUtility",
|
|
728
|
+
"ForecastPortfolioUtilityBinding",
|
|
729
|
+
"ForecastPortfolioUtilityInput",
|
|
730
|
+
"ForecastQuantile",
|
|
731
|
+
"PortfolioAllocationScore",
|
|
732
|
+
"validate_action_portfolio_decision",
|
|
733
|
+
]
|