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,363 @@
|
|
|
1
|
+
"""Composition portfolio selection: span the proposal sources, choose within at random.
|
|
2
|
+
|
|
3
|
+
This is a *product* policy, not an experiment arm. It replaces the calibrated
|
|
4
|
+
score, the tie-break cascade and the forecast channel with one rule:
|
|
5
|
+
|
|
6
|
+
spend the budget so that it spans the proposal sources, and pick uniformly
|
|
7
|
+
inside each source.
|
|
8
|
+
|
|
9
|
+
The evidence for that rule, in one line: enforcing the family composition the
|
|
10
|
+
allocator was already under captures 0.801 of a clairvoyant oracle while the
|
|
11
|
+
model's own top-k captures 0.748, measured over 36 waves with every 4-subset of
|
|
12
|
+
8 enumerated exactly; and the two proposal sources are complementary in kind --
|
|
13
|
+
the model supplies hit rate, the numerical engine supplies the rare large hit
|
|
14
|
+
that carries a heavy-tailed total. What makes a slate good is that it spans the
|
|
15
|
+
sources, not that it is ordered well within them.
|
|
16
|
+
|
|
17
|
+
Two properties this policy deliberately has
|
|
18
|
+
-------------------------------------------
|
|
19
|
+
**It makes no provider call.** Selection is arithmetic over the sealed finite
|
|
20
|
+
contract. A campaign running this policy spends model budget on *generation*
|
|
21
|
+
only.
|
|
22
|
+
|
|
23
|
+
**Its composition is derived, never hard-coded.** Baking in the family mix
|
|
24
|
+
observed on five research domains would ship a constant fitted to those domains
|
|
25
|
+
and call it general. Instead the caller injects
|
|
26
|
+
:class:`CompositionObservationSource`, which supplies realized family shares
|
|
27
|
+
from prior sealed campaigns; the policy apportions the budget to those shares by
|
|
28
|
+
largest remainder. With no observations it falls back to *spanning*, which has
|
|
29
|
+
no parameters at all: one seat per distinct family before any family repeats.
|
|
30
|
+
Replay evidence that this transfers rather than overfits: proportions estimated
|
|
31
|
+
leave-one-market-out scored 0.22815 against 0.23202 for proportions taken from
|
|
32
|
+
the market's own realized multiset.
|
|
33
|
+
|
|
34
|
+
Determinism
|
|
35
|
+
-----------
|
|
36
|
+
"Uniform within" is drawn from a generator seeded by the request's own identity
|
|
37
|
+
(the finite contract identity and the call id), so a decision is reproducible
|
|
38
|
+
from receipts and two runs of the same campaign make the same choice. The
|
|
39
|
+
policy is a pure function of its inputs; it holds no mutable state.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
import hashlib
|
|
45
|
+
import random
|
|
46
|
+
from dataclasses import dataclass
|
|
47
|
+
from typing import Protocol, runtime_checkable
|
|
48
|
+
|
|
49
|
+
from agent_evolve.domain.finite_variation import FiniteVariationOption
|
|
50
|
+
from agent_evolve.ports.agentic_generator import (
|
|
51
|
+
MetricEffectDirection,
|
|
52
|
+
MetricEffectPrediction,
|
|
53
|
+
)
|
|
54
|
+
from agent_evolve.ports.portfolio_selection import (
|
|
55
|
+
PortfolioMemberDraft,
|
|
56
|
+
PortfolioSelectionRequest,
|
|
57
|
+
PortfolioSelectionResult,
|
|
58
|
+
pairwise_disjoint_parent_patch_witness,
|
|
59
|
+
resolve_ranked_portfolio_decision,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
COMPOSITION_PORTFOLIO_POLICY_ID = "composition_spanning_portfolio"
|
|
63
|
+
COMPOSITION_PORTFOLIO_POLICY_VERSION = 1
|
|
64
|
+
|
|
65
|
+
# The identity a receipt pins. It covers the rule, not the observations: the
|
|
66
|
+
# same policy fed different receipt-derived shares is the same policy.
|
|
67
|
+
COMPOSITION_PORTFOLIO_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
68
|
+
b"agent-evolve:composition-spanning-portfolio:v1\x00"
|
|
69
|
+
b"rule=span-sources-then-uniform-within;"
|
|
70
|
+
b"apportionment=largest-remainder;"
|
|
71
|
+
b"shares=injected-observations-or-uniform-spanning;"
|
|
72
|
+
b"provider-calls=0"
|
|
73
|
+
).hexdigest()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@runtime_checkable
|
|
77
|
+
class CompositionObservationSource(Protocol):
|
|
78
|
+
"""Realized family shares observed in prior sealed campaigns.
|
|
79
|
+
|
|
80
|
+
The contract is deliberately minimal: a mapping from family token to a
|
|
81
|
+
non-negative weight. Weights need not sum to one; only their ratios matter.
|
|
82
|
+
An empty mapping means "no observations", and the policy falls back to
|
|
83
|
+
parameter-free spanning rather than inventing a mix.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def family_shares(self) -> dict[str, float]: ...
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass(frozen=True, slots=True)
|
|
90
|
+
class FrozenCompositionObservations:
|
|
91
|
+
"""A concrete observation source, e.g. built leave-one-campaign-out."""
|
|
92
|
+
|
|
93
|
+
shares: tuple[tuple[str, float], ...] = ()
|
|
94
|
+
|
|
95
|
+
def __post_init__(self) -> None:
|
|
96
|
+
if type(self.shares) is not tuple:
|
|
97
|
+
raise TypeError("shares must be an exact tuple")
|
|
98
|
+
seen = set()
|
|
99
|
+
for entry in self.shares:
|
|
100
|
+
if type(entry) is not tuple or len(entry) != 2:
|
|
101
|
+
raise TypeError("each share must be an exact (family, weight) pair")
|
|
102
|
+
family, weight = entry
|
|
103
|
+
if type(family) is not str or not family:
|
|
104
|
+
raise ValueError("family must be non-empty text")
|
|
105
|
+
if type(weight) is not float or weight < 0.0 or weight != weight:
|
|
106
|
+
raise ValueError("weight must be a non-negative finite float")
|
|
107
|
+
if family in seen:
|
|
108
|
+
raise ValueError("shares repeat a family")
|
|
109
|
+
seen.add(family)
|
|
110
|
+
|
|
111
|
+
def family_shares(self) -> dict[str, float]:
|
|
112
|
+
return {family: weight for family, weight in self.shares}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _seat_apportionment(
|
|
116
|
+
families: list[str],
|
|
117
|
+
shares: dict[str, float],
|
|
118
|
+
budget: int,
|
|
119
|
+
capacity: dict[str, int],
|
|
120
|
+
) -> dict[str, int]:
|
|
121
|
+
"""Largest-remainder apportionment of `budget` seats over `families`.
|
|
122
|
+
|
|
123
|
+
Deterministic given the inputs. Never assigns a family more seats than it
|
|
124
|
+
has options, and never leaves the budget unfilled while capacity remains.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
weights = {f: max(float(shares.get(f, 0.0)), 0.0) for f in families}
|
|
128
|
+
total = sum(weights.values())
|
|
129
|
+
if total <= 0.0:
|
|
130
|
+
# no usable observations: spanning, i.e. every family weighted alike
|
|
131
|
+
weights = {f: 1.0 for f in families}
|
|
132
|
+
total = float(len(families))
|
|
133
|
+
|
|
134
|
+
quota = {f: budget * weights[f] / total for f in families}
|
|
135
|
+
seats = {f: min(int(quota[f]), capacity[f]) for f in families}
|
|
136
|
+
|
|
137
|
+
# distribute the remainder by largest fractional part, then by family token
|
|
138
|
+
# so the result cannot depend on dict ordering
|
|
139
|
+
while sum(seats.values()) < budget:
|
|
140
|
+
eligible = [f for f in families if seats[f] < capacity[f]]
|
|
141
|
+
if not eligible:
|
|
142
|
+
break
|
|
143
|
+
eligible.sort(key=lambda f: (-(quota[f] - seats[f]), f))
|
|
144
|
+
seats[eligible[0]] += 1
|
|
145
|
+
return seats
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _spanning_order(
|
|
149
|
+
families: list[str],
|
|
150
|
+
capacity: dict[str, int],
|
|
151
|
+
budget: int,
|
|
152
|
+
rng: random.Random,
|
|
153
|
+
) -> dict[str, int]:
|
|
154
|
+
"""One seat per distinct family before any family repeats. No parameters."""
|
|
155
|
+
|
|
156
|
+
order = sorted(families)
|
|
157
|
+
rng.shuffle(order)
|
|
158
|
+
seats = {f: 0 for f in families}
|
|
159
|
+
while sum(seats.values()) < budget:
|
|
160
|
+
progressed = False
|
|
161
|
+
for family in order:
|
|
162
|
+
if sum(seats.values()) >= budget:
|
|
163
|
+
break
|
|
164
|
+
if seats[family] < capacity[family]:
|
|
165
|
+
seats[family] += 1
|
|
166
|
+
progressed = True
|
|
167
|
+
if not progressed:
|
|
168
|
+
break
|
|
169
|
+
return seats
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _declined_predictions(
|
|
173
|
+
metric_ids: tuple[str, ...],
|
|
174
|
+
) -> tuple[MetricEffectPrediction, ...]:
|
|
175
|
+
"""An explicit non-forecast for every metric the port requires.
|
|
176
|
+
|
|
177
|
+
The port's schema mandates a prediction per required metric. This policy has
|
|
178
|
+
no forecast channel, so it declines rather than inventing a direction:
|
|
179
|
+
every prediction is UNKNOWN. That is deliberate and is what makes the
|
|
180
|
+
absence of a forecast channel visible in the receipt instead of hidden.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
return tuple(
|
|
184
|
+
MetricEffectPrediction(
|
|
185
|
+
metric_id=metric_id,
|
|
186
|
+
direction=MetricEffectDirection.UNKNOWN,
|
|
187
|
+
)
|
|
188
|
+
for metric_id in metric_ids
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@dataclass(frozen=True, slots=True)
|
|
193
|
+
class CompositionPortfolioSelectionPolicy:
|
|
194
|
+
"""Span the proposal sources; choose uniformly within them.
|
|
195
|
+
|
|
196
|
+
``observations`` is optional. Supplied, the budget is apportioned to the
|
|
197
|
+
observed family shares; omitted, the policy spans families evenly, which is
|
|
198
|
+
parameter-free.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
observations: CompositionObservationSource | None = None
|
|
202
|
+
|
|
203
|
+
def _rng(self, request: PortfolioSelectionRequest) -> random.Random:
|
|
204
|
+
seed = hashlib.sha256(
|
|
205
|
+
b"agent-evolve:composition-portfolio-draw:v1\x00"
|
|
206
|
+
+ request.finite_variation_contract.identity_sha256.encode("ascii")
|
|
207
|
+
+ b"\x00"
|
|
208
|
+
+ request.call_id.value.encode("ascii")
|
|
209
|
+
).digest()
|
|
210
|
+
return random.Random(int.from_bytes(seed[:16], "big"))
|
|
211
|
+
|
|
212
|
+
def _choose(
|
|
213
|
+
self, request: PortfolioSelectionRequest
|
|
214
|
+
) -> tuple[FiniteVariationOption, ...]:
|
|
215
|
+
contract = request.finite_variation_contract
|
|
216
|
+
budget = request.portfolio_size
|
|
217
|
+
rng = self._rng(request)
|
|
218
|
+
|
|
219
|
+
by_family: dict[str, list[FiniteVariationOption]] = {}
|
|
220
|
+
for option in contract.options:
|
|
221
|
+
by_family.setdefault(option.family, []).append(option)
|
|
222
|
+
for options in by_family.values():
|
|
223
|
+
options.sort(key=lambda o: o.option_id)
|
|
224
|
+
families = sorted(by_family)
|
|
225
|
+
capacity = {f: len(by_family[f]) for f in families}
|
|
226
|
+
|
|
227
|
+
chosen: list[FiniteVariationOption] = []
|
|
228
|
+
taken: set[str] = set()
|
|
229
|
+
|
|
230
|
+
# options the engine requires in the pool are honoured first: the
|
|
231
|
+
# policy adds composition, it does not override a hard constraint
|
|
232
|
+
required = set(request.candidate_pool_required_option_ids)
|
|
233
|
+
for option in contract.options:
|
|
234
|
+
if option.option_id in required and len(chosen) < budget:
|
|
235
|
+
chosen.append(option)
|
|
236
|
+
taken.add(option.option_id)
|
|
237
|
+
|
|
238
|
+
remaining = budget - len(chosen)
|
|
239
|
+
if remaining > 0:
|
|
240
|
+
free_capacity = {
|
|
241
|
+
f: sum(1 for o in by_family[f] if o.option_id not in taken)
|
|
242
|
+
for f in families
|
|
243
|
+
}
|
|
244
|
+
shares = self.observations.family_shares() if self.observations else {}
|
|
245
|
+
usable = {f: w for f, w in shares.items() if f in free_capacity}
|
|
246
|
+
if usable and sum(usable.values()) > 0.0:
|
|
247
|
+
seats = _seat_apportionment(
|
|
248
|
+
families, usable, remaining, free_capacity
|
|
249
|
+
)
|
|
250
|
+
else:
|
|
251
|
+
seats = _spanning_order(families, free_capacity, remaining, rng)
|
|
252
|
+
|
|
253
|
+
for family in sorted(families):
|
|
254
|
+
pool = [o for o in by_family[family] if o.option_id not in taken]
|
|
255
|
+
take = min(seats.get(family, 0), len(pool))
|
|
256
|
+
if take <= 0:
|
|
257
|
+
continue
|
|
258
|
+
for option in rng.sample(pool, take):
|
|
259
|
+
chosen.append(option)
|
|
260
|
+
taken.add(option.option_id)
|
|
261
|
+
|
|
262
|
+
# top up if apportionment could not fill the budget (small catalogs)
|
|
263
|
+
if len(chosen) < budget:
|
|
264
|
+
pool = [o for o in contract.options if o.option_id not in taken]
|
|
265
|
+
rng.shuffle(pool)
|
|
266
|
+
for option in pool[: budget - len(chosen)]:
|
|
267
|
+
chosen.append(option)
|
|
268
|
+
taken.add(option.option_id)
|
|
269
|
+
|
|
270
|
+
if len(chosen) != budget:
|
|
271
|
+
raise ValueError(
|
|
272
|
+
"composition policy could not fill the portfolio from the contract"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
if request.require_pairwise_disjoint_parent_patches:
|
|
276
|
+
chosen = self._project_to_legal(request, chosen)
|
|
277
|
+
return tuple(chosen)
|
|
278
|
+
|
|
279
|
+
@staticmethod
|
|
280
|
+
def _project_to_legal(
|
|
281
|
+
request: PortfolioSelectionRequest,
|
|
282
|
+
chosen: list[FiniteVariationOption],
|
|
283
|
+
) -> list[FiniteVariationOption]:
|
|
284
|
+
"""Restrict the slate to one the port will accept.
|
|
285
|
+
|
|
286
|
+
LEGALITY ONLY. The witness is the port's own feasibility search over the
|
|
287
|
+
sealed parent-relative patches; the port documents it as a structural
|
|
288
|
+
decoding aid that "proves how to satisfy the hard combinatorial contract
|
|
289
|
+
without ranking an option by objective quality". The composition order
|
|
290
|
+
is handed to it as ``preferred_option_ids``, so the projection keeps as
|
|
291
|
+
much of the composition as legality allows and changes nothing else.
|
|
292
|
+
|
|
293
|
+
This is not a quality heuristic and must never become one: the deployed
|
|
294
|
+
allocator is already subject to this same rule, and adding anything that
|
|
295
|
+
also picks better would change the treatment.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
contract = request.finite_variation_contract
|
|
299
|
+
witness = pairwise_disjoint_parent_patch_witness(
|
|
300
|
+
contract,
|
|
301
|
+
tuple(option.option_id for option in contract.options),
|
|
302
|
+
portfolio_size=request.portfolio_size,
|
|
303
|
+
min_distinct_families=request.min_distinct_families,
|
|
304
|
+
preferred_option_ids=tuple(option.option_id for option in chosen),
|
|
305
|
+
required_option_ids=tuple(request.candidate_pool_required_option_ids),
|
|
306
|
+
)
|
|
307
|
+
if witness is None:
|
|
308
|
+
raise ValueError(
|
|
309
|
+
"the finite contract admits no pairwise-disjoint portfolio of "
|
|
310
|
+
"the requested size"
|
|
311
|
+
)
|
|
312
|
+
by_id = {option.option_id: option for option in contract.options}
|
|
313
|
+
return [by_id[option_id] for option_id in witness]
|
|
314
|
+
|
|
315
|
+
async def select(
|
|
316
|
+
self, request: PortfolioSelectionRequest
|
|
317
|
+
) -> PortfolioSelectionResult:
|
|
318
|
+
if type(request) is not PortfolioSelectionRequest:
|
|
319
|
+
raise TypeError("request must be an exact PortfolioSelectionRequest")
|
|
320
|
+
request.__post_init__()
|
|
321
|
+
|
|
322
|
+
options = self._choose(request)
|
|
323
|
+
card_keys = tuple(sorted({card.card_key for card in request.cards}))
|
|
324
|
+
supporting = card_keys[:1] if request.require_supporting_cards else ()
|
|
325
|
+
|
|
326
|
+
drafts = tuple(
|
|
327
|
+
PortfolioMemberDraft(
|
|
328
|
+
option_id=option.option_id,
|
|
329
|
+
supporting_card_keys=supporting,
|
|
330
|
+
effect_predictions=_declined_predictions(
|
|
331
|
+
request.required_metric_ids
|
|
332
|
+
),
|
|
333
|
+
design_rationale=(
|
|
334
|
+
"composition policy: seat apportioned to proposal source "
|
|
335
|
+
f"{option.family!r}, member drawn uniformly within it; no "
|
|
336
|
+
"score, no tie-break, no forecast"
|
|
337
|
+
),
|
|
338
|
+
)
|
|
339
|
+
for option in options
|
|
340
|
+
)
|
|
341
|
+
decision = resolve_ranked_portfolio_decision(
|
|
342
|
+
request,
|
|
343
|
+
drafts,
|
|
344
|
+
policy_id=COMPOSITION_PORTFOLIO_POLICY_ID,
|
|
345
|
+
policy_version=COMPOSITION_PORTFOLIO_POLICY_VERSION,
|
|
346
|
+
policy_definition_sha256=COMPOSITION_PORTFOLIO_POLICY_DEFINITION_SHA256,
|
|
347
|
+
)
|
|
348
|
+
# provider_free is asserted, not left to be inferred from a missing
|
|
349
|
+
# telemetry field. The runtime verifies the assertion against the
|
|
350
|
+
# outbound journals and fails the wave if any call was observed.
|
|
351
|
+
return PortfolioSelectionResult(
|
|
352
|
+
decision=decision, telemetry=None, provider_free=True
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
__all__ = [
|
|
357
|
+
"COMPOSITION_PORTFOLIO_POLICY_DEFINITION_SHA256",
|
|
358
|
+
"COMPOSITION_PORTFOLIO_POLICY_ID",
|
|
359
|
+
"COMPOSITION_PORTFOLIO_POLICY_VERSION",
|
|
360
|
+
"CompositionObservationSource",
|
|
361
|
+
"CompositionPortfolioSelectionPolicy",
|
|
362
|
+
"FrozenCompositionObservations",
|
|
363
|
+
]
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Failure-contained execution for one concurrent application stage.
|
|
2
|
+
|
|
3
|
+
``asyncio.gather`` propagates the first child failure without cancelling the
|
|
4
|
+
remaining children. That behavior is unsafe at an application-stage
|
|
5
|
+
boundary: a failed model arm can leave sibling provider calls running after
|
|
6
|
+
the campaign has started finalization. This module owns the generic policy
|
|
7
|
+
for fail-fast sibling cancellation and bounded terminal-state retrieval.
|
|
8
|
+
|
|
9
|
+
The helper is deliberately workload- and transport-neutral. Child
|
|
10
|
+
coroutines remain responsible for retiring resources they own when cancelled;
|
|
11
|
+
the barrier guarantees that cancellation is delivered and that every child
|
|
12
|
+
which reaches a terminal state is observed before the stage exception exits.
|
|
13
|
+
Cancellation-resistant children are observed by a terminal callback after the
|
|
14
|
+
bounded drain interval, so their later exception can never become an
|
|
15
|
+
unretrieved task exception.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import asyncio
|
|
21
|
+
from collections.abc import Awaitable, Iterable
|
|
22
|
+
from contextlib import suppress
|
|
23
|
+
from typing import TypeVar
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
StageResultT = TypeVar("StageResultT")
|
|
27
|
+
|
|
28
|
+
NANOSECONDS_PER_SECOND = 1_000_000_000
|
|
29
|
+
DEFAULT_CONCURRENT_STAGE_CANCEL_DRAIN_TIMEOUT_NS = 5 * NANOSECONDS_PER_SECOND
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _consume_terminal(task: "asyncio.Future[object]") -> None:
|
|
33
|
+
"""Retrieve a terminal child's exception without retaining its content."""
|
|
34
|
+
|
|
35
|
+
if not task.done(): # pragma: no cover - callback/validated caller contract.
|
|
36
|
+
return
|
|
37
|
+
with suppress(BaseException):
|
|
38
|
+
task.exception()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def _cancel_and_drain(
|
|
42
|
+
tasks: tuple["asyncio.Future[object]", ...],
|
|
43
|
+
*,
|
|
44
|
+
timeout_ns: int,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Cancel live children and observe them for at most ``timeout_ns``."""
|
|
47
|
+
|
|
48
|
+
for task in tasks:
|
|
49
|
+
if not task.done():
|
|
50
|
+
task.cancel()
|
|
51
|
+
if not tasks:
|
|
52
|
+
return
|
|
53
|
+
done, pending = await asyncio.wait(
|
|
54
|
+
tasks,
|
|
55
|
+
timeout=timeout_ns / NANOSECONDS_PER_SECOND,
|
|
56
|
+
return_when=asyncio.ALL_COMPLETED,
|
|
57
|
+
)
|
|
58
|
+
for task in done:
|
|
59
|
+
_consume_terminal(task)
|
|
60
|
+
for task in pending:
|
|
61
|
+
# A transport stack can resist cancellation while it retires a socket.
|
|
62
|
+
# Do not let that defeat the campaign cleanup bound, but retain explicit
|
|
63
|
+
# ownership of its eventual terminal state.
|
|
64
|
+
task.add_done_callback(_consume_terminal)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def gather_concurrent_stage(
|
|
68
|
+
awaitables: Iterable[Awaitable[StageResultT]],
|
|
69
|
+
*,
|
|
70
|
+
cancel_drain_timeout_ns: int = (
|
|
71
|
+
DEFAULT_CONCURRENT_STAGE_CANCEL_DRAIN_TIMEOUT_NS
|
|
72
|
+
),
|
|
73
|
+
) -> tuple[StageResultT, ...]:
|
|
74
|
+
"""Return ordered results or fail after cancelling and draining siblings.
|
|
75
|
+
|
|
76
|
+
Success preserves input ordering. On the first observed child failure,
|
|
77
|
+
every still-live sibling is cancelled and terminal states are retrieved
|
|
78
|
+
before that failure is re-raised. Drain time is bounded because provider
|
|
79
|
+
cancellation must never hold campaign finalization hostage indefinitely.
|
|
80
|
+
|
|
81
|
+
If the caller itself is cancelled, the same cancel-and-drain law is applied
|
|
82
|
+
before ``CancelledError`` propagates.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
if (
|
|
86
|
+
type(cancel_drain_timeout_ns) is not int
|
|
87
|
+
or cancel_drain_timeout_ns <= 0
|
|
88
|
+
):
|
|
89
|
+
raise ValueError("cancel_drain_timeout_ns must be a positive exact integer")
|
|
90
|
+
tasks = tuple(asyncio.ensure_future(awaitable) for awaitable in awaitables)
|
|
91
|
+
if not tasks:
|
|
92
|
+
return ()
|
|
93
|
+
|
|
94
|
+
pending = set(tasks)
|
|
95
|
+
try:
|
|
96
|
+
while pending:
|
|
97
|
+
done, pending = await asyncio.wait(
|
|
98
|
+
pending,
|
|
99
|
+
return_when=asyncio.FIRST_COMPLETED,
|
|
100
|
+
)
|
|
101
|
+
failed = tuple(
|
|
102
|
+
task
|
|
103
|
+
for task in tasks
|
|
104
|
+
if task in done
|
|
105
|
+
and (
|
|
106
|
+
task.cancelled()
|
|
107
|
+
or task.exception() is not None
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
if not failed:
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
primary = failed[0]
|
|
114
|
+
primary_error: BaseException
|
|
115
|
+
if primary.cancelled():
|
|
116
|
+
primary_error = asyncio.CancelledError()
|
|
117
|
+
else:
|
|
118
|
+
observed = primary.exception()
|
|
119
|
+
assert observed is not None
|
|
120
|
+
primary_error = observed
|
|
121
|
+
|
|
122
|
+
# Retrieve every failure that completed in the same event-loop
|
|
123
|
+
# turn, not only the deterministic first one.
|
|
124
|
+
for task in done:
|
|
125
|
+
_consume_terminal(task)
|
|
126
|
+
await _cancel_and_drain(
|
|
127
|
+
tuple(task for task in tasks if task not in done),
|
|
128
|
+
timeout_ns=cancel_drain_timeout_ns,
|
|
129
|
+
)
|
|
130
|
+
raise primary_error
|
|
131
|
+
except asyncio.CancelledError:
|
|
132
|
+
await _cancel_and_drain(
|
|
133
|
+
tasks,
|
|
134
|
+
timeout_ns=cancel_drain_timeout_ns,
|
|
135
|
+
)
|
|
136
|
+
raise
|
|
137
|
+
|
|
138
|
+
return tuple(task.result() for task in tasks)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
__all__ = [
|
|
142
|
+
"DEFAULT_CONCURRENT_STAGE_CANCEL_DRAIN_TIMEOUT_NS",
|
|
143
|
+
"gather_concurrent_stage",
|
|
144
|
+
]
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Lower prior-only contextual decisions into generic allocator constraints.
|
|
2
|
+
|
|
3
|
+
This bridge deliberately knows only the sealed finite variation contract and
|
|
4
|
+
the generic source/operator attribution ports. It is the single seam between
|
|
5
|
+
the adaptive search controller and any trusted action allocator; no workload,
|
|
6
|
+
model, provider, prompt, or objective branch is permitted here.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from agent_evolve.application.action_structural_signature import (
|
|
12
|
+
parent_relative_changed_paths_by_option,
|
|
13
|
+
)
|
|
14
|
+
from agent_evolve.domain.finite_variation import FiniteVariationContract
|
|
15
|
+
from agent_evolve.ports.action_allocation import ExactActionArmCountConstraint
|
|
16
|
+
from agent_evolve.ports.portfolio_selection import (
|
|
17
|
+
pairwise_disjoint_parent_patch_pairs,
|
|
18
|
+
)
|
|
19
|
+
from agent_evolve.ports.contextual_search_allocation import (
|
|
20
|
+
ContextualPortfolioAllocationContract,
|
|
21
|
+
ContextualPortfolioAllocationRealization,
|
|
22
|
+
)
|
|
23
|
+
from agent_evolve.ports.variation_source import (
|
|
24
|
+
finite_variation_operator_id,
|
|
25
|
+
finite_variation_source_id,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def contextual_action_arm_count_constraints(
|
|
30
|
+
*,
|
|
31
|
+
finite_contract: FiniteVariationContract,
|
|
32
|
+
allocation: ContextualPortfolioAllocationContract | None,
|
|
33
|
+
portfolio_size: int,
|
|
34
|
+
) -> tuple[ExactActionArmCountConstraint, ...]:
|
|
35
|
+
"""Return exact source/operator marginals for one finite action request."""
|
|
36
|
+
|
|
37
|
+
if type(finite_contract) is not FiniteVariationContract:
|
|
38
|
+
raise TypeError("finite_contract must be an exact FiniteVariationContract")
|
|
39
|
+
finite_contract.__post_init__()
|
|
40
|
+
if type(portfolio_size) is not int or portfolio_size <= 0:
|
|
41
|
+
raise ValueError("portfolio_size must be a positive exact integer")
|
|
42
|
+
if allocation is None:
|
|
43
|
+
return ()
|
|
44
|
+
if type(allocation) is not ContextualPortfolioAllocationContract:
|
|
45
|
+
raise TypeError("allocation must be an exact contextual contract or None")
|
|
46
|
+
allocation.__post_init__()
|
|
47
|
+
if allocation.evaluation_slots != portfolio_size:
|
|
48
|
+
raise ValueError("contextual allocation differs from portfolio capacity")
|
|
49
|
+
|
|
50
|
+
source_rows = tuple(
|
|
51
|
+
sorted(
|
|
52
|
+
(option.option_id, finite_variation_source_id(option))
|
|
53
|
+
for option in finite_contract.options
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
operator_rows = tuple(
|
|
57
|
+
sorted(
|
|
58
|
+
(option.option_id, finite_variation_operator_id(option))
|
|
59
|
+
for option in finite_contract.options
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
constraints = (
|
|
63
|
+
ExactActionArmCountConstraint(
|
|
64
|
+
constraint_id="operator",
|
|
65
|
+
option_arm_ids=operator_rows,
|
|
66
|
+
target_counts=allocation.operator_target_counts,
|
|
67
|
+
),
|
|
68
|
+
ExactActionArmCountConstraint(
|
|
69
|
+
constraint_id="source",
|
|
70
|
+
option_arm_ids=source_rows,
|
|
71
|
+
target_counts=allocation.source_target_counts,
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
return tuple(sorted(constraints, key=lambda value: value.constraint_id))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def contextual_allocation_realization(
|
|
78
|
+
*,
|
|
79
|
+
finite_contract: FiniteVariationContract,
|
|
80
|
+
allocation: ContextualPortfolioAllocationContract | None,
|
|
81
|
+
selected_option_ids: tuple[str, ...],
|
|
82
|
+
) -> ContextualPortfolioAllocationRealization | None:
|
|
83
|
+
"""Project selected sealed actions into objective-blind controller credit."""
|
|
84
|
+
|
|
85
|
+
if type(finite_contract) is not FiniteVariationContract:
|
|
86
|
+
raise TypeError("finite_contract must be an exact FiniteVariationContract")
|
|
87
|
+
finite_contract.__post_init__()
|
|
88
|
+
if allocation is None:
|
|
89
|
+
return None
|
|
90
|
+
if type(allocation) is not ContextualPortfolioAllocationContract:
|
|
91
|
+
raise TypeError("allocation must be an exact contextual contract or None")
|
|
92
|
+
allocation.__post_init__()
|
|
93
|
+
if (
|
|
94
|
+
type(selected_option_ids) is not tuple
|
|
95
|
+
or len(selected_option_ids) != allocation.evaluation_slots
|
|
96
|
+
or selected_option_ids != tuple(dict.fromkeys(selected_option_ids))
|
|
97
|
+
):
|
|
98
|
+
raise ValueError("selected_option_ids must be an exact unique K-tuple")
|
|
99
|
+
by_id = {value.option_id: value for value in finite_contract.options}
|
|
100
|
+
if not set(selected_option_ids).issubset(by_id):
|
|
101
|
+
raise ValueError("selected option escapes the finite variation contract")
|
|
102
|
+
|
|
103
|
+
def realized(
|
|
104
|
+
target: tuple[tuple[str, int], ...],
|
|
105
|
+
projector,
|
|
106
|
+
) -> tuple[tuple[str, int], ...]:
|
|
107
|
+
counts = {arm_id: 0 for arm_id, _ in target}
|
|
108
|
+
for option_id in selected_option_ids:
|
|
109
|
+
arm_id = projector(by_id[option_id])
|
|
110
|
+
if arm_id not in counts:
|
|
111
|
+
raise ValueError("selected option carries an unrequested arm")
|
|
112
|
+
counts[arm_id] += 1
|
|
113
|
+
return tuple(sorted(counts.items()))
|
|
114
|
+
|
|
115
|
+
receipt = ContextualPortfolioAllocationRealization(
|
|
116
|
+
campaign_scope_sha256=allocation.campaign_scope_sha256,
|
|
117
|
+
query_sha256=allocation.query_sha256,
|
|
118
|
+
decision_sha256=allocation.decision_sha256,
|
|
119
|
+
contract_sha256=allocation.contract_sha256,
|
|
120
|
+
controller_wave_index=allocation.controller_wave_index,
|
|
121
|
+
slice_id=allocation.slice_id,
|
|
122
|
+
requested_source_target_counts=allocation.source_target_counts,
|
|
123
|
+
requested_operator_target_counts=allocation.operator_target_counts,
|
|
124
|
+
realized_source_target_counts=realized(
|
|
125
|
+
allocation.source_target_counts,
|
|
126
|
+
finite_variation_source_id,
|
|
127
|
+
),
|
|
128
|
+
realized_operator_target_counts=realized(
|
|
129
|
+
allocation.operator_target_counts,
|
|
130
|
+
finite_variation_operator_id,
|
|
131
|
+
),
|
|
132
|
+
requested_minimum_single_path_interventions=(
|
|
133
|
+
allocation.minimum_single_path_interventions
|
|
134
|
+
),
|
|
135
|
+
realized_single_path_interventions=sum(
|
|
136
|
+
len(paths) == 1
|
|
137
|
+
for option_id, paths in parent_relative_changed_paths_by_option(
|
|
138
|
+
finite_contract
|
|
139
|
+
).items()
|
|
140
|
+
if option_id in selected_option_ids
|
|
141
|
+
),
|
|
142
|
+
requested_minimum_disjoint_parent_patch_pairs=(
|
|
143
|
+
allocation.minimum_disjoint_parent_patch_pairs
|
|
144
|
+
),
|
|
145
|
+
realized_disjoint_parent_patch_pairs=len(
|
|
146
|
+
pairwise_disjoint_parent_patch_pairs(
|
|
147
|
+
finite_contract,
|
|
148
|
+
selected_option_ids,
|
|
149
|
+
)
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
receipt.require_contract(allocation)
|
|
153
|
+
return receipt
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def selected_variation_source_ids(
|
|
157
|
+
*,
|
|
158
|
+
finite_contract: FiniteVariationContract,
|
|
159
|
+
selected_option_ids: tuple[str, ...],
|
|
160
|
+
) -> tuple[str, ...]:
|
|
161
|
+
"""Return sealed proposal-source labels in evaluated member order."""
|
|
162
|
+
|
|
163
|
+
if type(finite_contract) is not FiniteVariationContract:
|
|
164
|
+
raise TypeError("finite_contract must be an exact FiniteVariationContract")
|
|
165
|
+
finite_contract.__post_init__()
|
|
166
|
+
if type(selected_option_ids) is not tuple:
|
|
167
|
+
raise TypeError("selected_option_ids must be an exact tuple")
|
|
168
|
+
by_id = {value.option_id: value for value in finite_contract.options}
|
|
169
|
+
if not set(selected_option_ids).issubset(by_id):
|
|
170
|
+
raise ValueError("selected option escapes the finite variation contract")
|
|
171
|
+
return tuple(
|
|
172
|
+
finite_variation_source_id(by_id[option_id])
|
|
173
|
+
for option_id in selected_option_ids
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
__all__ = [
|
|
178
|
+
"contextual_action_arm_count_constraints",
|
|
179
|
+
"contextual_allocation_realization",
|
|
180
|
+
"selected_variation_source_ids",
|
|
181
|
+
]
|