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,167 @@
|
|
|
1
|
+
"""Problem protocol, objective specification, and validation outcome."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from numbers import Real
|
|
9
|
+
from typing import Any, Dict, Literal, Optional, Protocol, Sequence, TypeVar, runtime_checkable
|
|
10
|
+
|
|
11
|
+
Goal = Literal["min", "max"]
|
|
12
|
+
ConfigT = TypeVar("ConfigT")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class ObjectiveSpec:
|
|
17
|
+
"""Specification for a single optimisation objective.
|
|
18
|
+
|
|
19
|
+
``description`` is the objective's MEANING -- what the number measures,
|
|
20
|
+
its units, what a good value looks like ("spec-attainment reward, sum of
|
|
21
|
+
nine clipped terms, maximised at 0 = every spec met"). It is rendered
|
|
22
|
+
into every model-facing prompt: an optimizer asked to trade objectives
|
|
23
|
+
it cannot interpret is reasoning blindfolded, and the resulting failure
|
|
24
|
+
is unattributable (channel defect vs capability). Optional so existing
|
|
25
|
+
problems keep working; a problem that leaves it empty is telling the
|
|
26
|
+
model "the name is all you get".
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
name: str
|
|
30
|
+
goal: Goal
|
|
31
|
+
description: str = ""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ProblemContractError(RuntimeError):
|
|
35
|
+
"""The problem adapter violated its declared objective/evaluation contract."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def validate_objective_specs(objectives: Sequence[ObjectiveSpec]) -> None:
|
|
39
|
+
"""Validate objective declarations before any proposal or evaluation work."""
|
|
40
|
+
if not objectives:
|
|
41
|
+
raise ProblemContractError("Problem must define at least one objective")
|
|
42
|
+
names = [spec.name for spec in objectives]
|
|
43
|
+
if any(not isinstance(name, str) or not name.strip() for name in names):
|
|
44
|
+
raise ProblemContractError("Objective names must be non-empty strings")
|
|
45
|
+
duplicates = sorted({name for name in names if names.count(name) > 1})
|
|
46
|
+
if duplicates:
|
|
47
|
+
raise ProblemContractError(f"Duplicate objective name(s): {', '.join(duplicates)}")
|
|
48
|
+
invalid_goals = [f"{spec.name}={spec.goal!r}" for spec in objectives
|
|
49
|
+
if spec.goal not in ("min", "max")]
|
|
50
|
+
if invalid_goals:
|
|
51
|
+
raise ProblemContractError(
|
|
52
|
+
"Objective goals must be 'min' or 'max': " + ", ".join(invalid_goals)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def normalize_objective_values(
|
|
57
|
+
values: Any,
|
|
58
|
+
objectives: Sequence[ObjectiveSpec],
|
|
59
|
+
) -> Dict[str, float]:
|
|
60
|
+
"""Return a complete finite objective vector or raise ``ProblemContractError``.
|
|
61
|
+
|
|
62
|
+
Evaluators must return exactly the declared objectives. Diagnostics belong in
|
|
63
|
+
a separate adapter-level artifact/metadata channel; accepting undeclared keys
|
|
64
|
+
here would make misspelled objective names too easy to overlook.
|
|
65
|
+
"""
|
|
66
|
+
validate_objective_specs(objectives)
|
|
67
|
+
if not isinstance(values, Mapping):
|
|
68
|
+
raise ProblemContractError(
|
|
69
|
+
f"Problem.evaluate() must return a mapping, got {type(values).__name__}"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
expected = {spec.name for spec in objectives}
|
|
73
|
+
actual = set(values.keys())
|
|
74
|
+
missing = sorted(expected - actual)
|
|
75
|
+
extra = sorted(str(key) for key in actual - expected)
|
|
76
|
+
if missing or extra:
|
|
77
|
+
details = []
|
|
78
|
+
if missing:
|
|
79
|
+
details.append("missing: " + ", ".join(missing))
|
|
80
|
+
if extra:
|
|
81
|
+
details.append("undeclared: " + ", ".join(extra))
|
|
82
|
+
raise ProblemContractError("Invalid objective mapping (" + "; ".join(details) + ")")
|
|
83
|
+
|
|
84
|
+
normalized: Dict[str, float] = {}
|
|
85
|
+
for spec in objectives:
|
|
86
|
+
value = values[spec.name]
|
|
87
|
+
if isinstance(value, bool) or not isinstance(value, Real):
|
|
88
|
+
raise ProblemContractError(
|
|
89
|
+
f"Objective {spec.name!r} must be a real number, got {type(value).__name__}"
|
|
90
|
+
)
|
|
91
|
+
number = float(value)
|
|
92
|
+
if not math.isfinite(number):
|
|
93
|
+
raise ProblemContractError(
|
|
94
|
+
f"Objective {spec.name!r} must be finite, got {number!r}"
|
|
95
|
+
)
|
|
96
|
+
normalized[spec.name] = number
|
|
97
|
+
return normalized
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True)
|
|
101
|
+
class ValidationOutcome:
|
|
102
|
+
"""Structured result of a feasibility pre-check.
|
|
103
|
+
|
|
104
|
+
``failure_phase`` lets a problem label *where* a candidate broke (e.g.
|
|
105
|
+
``"structural" | "constraint" | "simulation"``) so the loop can feed richer
|
|
106
|
+
failure context to the LLM. ``message`` is forwarded verbatim to the model.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
ok: bool
|
|
110
|
+
failure_phase: Optional[str] = None
|
|
111
|
+
message: Optional[str] = None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@runtime_checkable
|
|
115
|
+
class Problem(Protocol[ConfigT]):
|
|
116
|
+
"""Minimal interface that every optimisation problem must satisfy.
|
|
117
|
+
|
|
118
|
+
Required
|
|
119
|
+
--------
|
|
120
|
+
objectives : Sequence[ObjectiveSpec]
|
|
121
|
+
The objectives to optimise (at least one).
|
|
122
|
+
evaluate(config) -> Dict[str, float]
|
|
123
|
+
Return exactly one finite numeric value for every declared objective.
|
|
124
|
+
Raise ``ValueError`` with a descriptive message only for invalid /
|
|
125
|
+
infeasible configurations -- the message is forwarded to the LLM as
|
|
126
|
+
feedback. Infrastructure and programming errors must use other exception
|
|
127
|
+
types and abort the run rather than becoming candidate feedback.
|
|
128
|
+
|
|
129
|
+
Optional (detected via ``hasattr`` at runtime)
|
|
130
|
+
----------------------------------------------
|
|
131
|
+
validate_detailed(config) -> ValidationOutcome
|
|
132
|
+
Structured feasibility pre-check carrying a ``failure_phase`` label.
|
|
133
|
+
validate(config) -> bool
|
|
134
|
+
Legacy boolean pre-check. **Raise** ``ValueError("...")`` when invalid
|
|
135
|
+
(never return ``False`` silently). Wrapped into a ``ValidationOutcome``.
|
|
136
|
+
search_space_description() -> str
|
|
137
|
+
Human-readable description of the configuration format, valid ranges,
|
|
138
|
+
and constraints. Included verbatim in LLM prompts.
|
|
139
|
+
render_candidate(config) -> str
|
|
140
|
+
Compact one-line summary of a configuration, used in failure / Pareto
|
|
141
|
+
lists shown to the LLM. Defaults to pretty JSON when absent.
|
|
142
|
+
candidate_key(config) -> str
|
|
143
|
+
Canonical key used to de-duplicate proposed candidates across the run
|
|
144
|
+
(so identical configs are not re-evaluated). Defaults to sorted JSON.
|
|
145
|
+
|
|
146
|
+
Optional attribute:
|
|
147
|
+
|
|
148
|
+
directives
|
|
149
|
+
A ``Directives`` provider supplying prompt wording for this problem.
|
|
150
|
+
When absent, the backbone's generic ``DefaultDirectives`` is used.
|
|
151
|
+
|
|
152
|
+
Optional attributes (not part of the protocol check):
|
|
153
|
+
|
|
154
|
+
candidate_model : type[pydantic.BaseModel]
|
|
155
|
+
Schema for one candidate; its JSON schema is shown to the LLM.
|
|
156
|
+
constraints_description : str
|
|
157
|
+
Extra free-text constraints injected into prompts.
|
|
158
|
+
example_config : dict
|
|
159
|
+
A reference configuration the model can imitate.
|
|
160
|
+
config_schema : dict
|
|
161
|
+
A pseudo-JSON schema for the configuration.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def objectives(self) -> Sequence[ObjectiveSpec]: ...
|
|
166
|
+
|
|
167
|
+
def evaluate(self, config: ConfigT) -> Dict[str, float]: ...
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""Result containers and Pareto-front utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from numbers import Real
|
|
8
|
+
from typing import Any, Dict, Generic, List, Optional, Sequence, Tuple, TypeVar
|
|
9
|
+
|
|
10
|
+
from agent_evolve.core.problem import ObjectiveSpec, ProblemContractError
|
|
11
|
+
from agent_evolve.core.telemetry import RunTelemetry
|
|
12
|
+
|
|
13
|
+
ConfigT = TypeVar("ConfigT")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class ProviderUsageSummary:
|
|
19
|
+
"""What the run spent, so a caller can answer "what did this cost me".
|
|
20
|
+
|
|
21
|
+
Counted from the calls actually made, never declared. ``calls`` is zero for
|
|
22
|
+
an uninformed proposer, and that zero is measured: a run that made no model
|
|
23
|
+
call still reports the block rather than omitting it, because an absent
|
|
24
|
+
field cannot be told apart from an unrecorded one.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
calls: int = 0
|
|
28
|
+
input_tokens: Optional[int] = None
|
|
29
|
+
output_tokens: Optional[int] = None
|
|
30
|
+
cost_usd: Optional[str] = None
|
|
31
|
+
model: Optional[str] = None
|
|
32
|
+
reported_by: Optional[str] = None
|
|
33
|
+
|
|
34
|
+
def __post_init__(self) -> None:
|
|
35
|
+
if type(self.calls) is not int or self.calls < 0:
|
|
36
|
+
raise ValueError("calls must be a non-negative integer")
|
|
37
|
+
figures = {
|
|
38
|
+
"input_tokens": self.input_tokens,
|
|
39
|
+
"output_tokens": self.output_tokens,
|
|
40
|
+
"cost_usd": self.cost_usd,
|
|
41
|
+
}
|
|
42
|
+
present = {name for name, value in figures.items() if value is not None}
|
|
43
|
+
if present and not self.reported_by:
|
|
44
|
+
# A figure with no reporter is a number nobody measured. Zero is the
|
|
45
|
+
# dangerous case: it reads as "nothing was spent" when it means "no
|
|
46
|
+
# one looked". Naming the reporter is what makes the difference
|
|
47
|
+
# unrepresentable rather than merely documented.
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"usage figures {sorted(present)} require reported_by naming "
|
|
50
|
+
"what measured them"
|
|
51
|
+
)
|
|
52
|
+
if self.reported_by is not None and not present:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"reported_by names a reporter that supplied no figure"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def provider_free(self) -> bool:
|
|
59
|
+
"""True only when calls were counted and none occurred."""
|
|
60
|
+
|
|
61
|
+
return self.calls == 0
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def cost_is_known(self) -> bool:
|
|
65
|
+
return self.cost_usd is not None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class Candidate(Generic[ConfigT]):
|
|
70
|
+
"""A single evaluated configuration."""
|
|
71
|
+
|
|
72
|
+
configuration: ConfigT
|
|
73
|
+
objectives: Dict[str, float]
|
|
74
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True)
|
|
78
|
+
class SearchResult(Generic[ConfigT]):
|
|
79
|
+
"""Aggregated output of an optimisation run.
|
|
80
|
+
|
|
81
|
+
Attributes
|
|
82
|
+
----------
|
|
83
|
+
objectives : objectives used during the run.
|
|
84
|
+
best : the single recommended candidate (minimax rank over the Pareto front;
|
|
85
|
+
see :func:`select_minimax_rank`).
|
|
86
|
+
pareto_front : non-dominated set.
|
|
87
|
+
all_candidates : every unique candidate processed across all generations,
|
|
88
|
+
including deterministic validation failures.
|
|
89
|
+
history : per-generation summary dicts.
|
|
90
|
+
best_per_generation : minimax-best candidate on the cumulative Pareto front
|
|
91
|
+
after each generation (same rule as ``best``); useful for progress.
|
|
92
|
+
evaluations : exact number of ``Problem.evaluate`` invocations, including
|
|
93
|
+
calls that raise candidate-level ``ValueError``. Deterministic pre-check
|
|
94
|
+
failures do not increment this evaluator-call budget.
|
|
95
|
+
telemetry : what each guidance mechanism did, plus the real/virtual
|
|
96
|
+
evaluation ledger. Populated by the genetic loop; ``None`` on paths
|
|
97
|
+
that have not adopted it yet.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
objectives: Sequence[ObjectiveSpec]
|
|
101
|
+
best: Candidate[ConfigT]
|
|
102
|
+
pareto_front: List[Candidate[ConfigT]] = field(default_factory=list)
|
|
103
|
+
all_candidates: List[Candidate[ConfigT]] = field(default_factory=list)
|
|
104
|
+
history: List[Dict[str, Any]] = field(default_factory=list)
|
|
105
|
+
best_per_generation: List[Candidate[ConfigT]] = field(default_factory=list)
|
|
106
|
+
evaluations: int = 0
|
|
107
|
+
provider_usage: "ProviderUsageSummary | None" = None
|
|
108
|
+
telemetry: Optional[RunTelemetry] = None
|
|
109
|
+
|
|
110
|
+
def candidates_by_author(self) -> Dict[str, int]:
|
|
111
|
+
"""How many candidates each authoring call produced.
|
|
112
|
+
|
|
113
|
+
Publish this beside any comparison that treats "the model proposed it"
|
|
114
|
+
as an arm. A count is checkable; a label is only asserted, and an arm
|
|
115
|
+
named for the component that produced it rather than the authority that
|
|
116
|
+
decided it is a mistake that survives review because the numbers still
|
|
117
|
+
look plausible.
|
|
118
|
+
"""
|
|
119
|
+
counts: Dict[str, int] = {}
|
|
120
|
+
for candidate in self.all_candidates:
|
|
121
|
+
author = candidate.metadata.get("authored_by", "unrecorded")
|
|
122
|
+
counts[author] = counts.get(author, 0) + 1
|
|
123
|
+
return counts
|
|
124
|
+
|
|
125
|
+
def proposed_candidates(self) -> List[Candidate[ConfigT]]:
|
|
126
|
+
"""Only what the proposer authored: the caller's own seeds excluded."""
|
|
127
|
+
|
|
128
|
+
return [
|
|
129
|
+
candidate
|
|
130
|
+
for candidate in self.all_candidates
|
|
131
|
+
if candidate.metadata.get("authored_by", "").startswith("proposer_")
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ------------------------------------------------------------------
|
|
136
|
+
# Pareto dominance
|
|
137
|
+
# ------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
def objective_value(values: Dict[str, float], name: str) -> float:
|
|
140
|
+
"""Read one required finite objective without fabricating a default value."""
|
|
141
|
+
if name not in values:
|
|
142
|
+
raise ProblemContractError(f"Missing declared objective {name!r}")
|
|
143
|
+
value = values[name]
|
|
144
|
+
if isinstance(value, bool) or not isinstance(value, Real):
|
|
145
|
+
raise ProblemContractError(
|
|
146
|
+
f"Objective {name!r} must be a real number, got {type(value).__name__}"
|
|
147
|
+
)
|
|
148
|
+
number = float(value)
|
|
149
|
+
if not math.isfinite(number):
|
|
150
|
+
raise ProblemContractError(f"Objective {name!r} must be finite, got {number!r}")
|
|
151
|
+
return number
|
|
152
|
+
|
|
153
|
+
def dominates(
|
|
154
|
+
a: Dict[str, float],
|
|
155
|
+
b: Dict[str, float],
|
|
156
|
+
objectives: Sequence[ObjectiveSpec],
|
|
157
|
+
) -> bool:
|
|
158
|
+
"""Return *True* if objective vector *a* Pareto-dominates *b*."""
|
|
159
|
+
all_geq = True
|
|
160
|
+
any_better = False
|
|
161
|
+
for spec in objectives:
|
|
162
|
+
va = objective_value(a, spec.name)
|
|
163
|
+
vb = objective_value(b, spec.name)
|
|
164
|
+
if spec.goal == "max":
|
|
165
|
+
if va < vb:
|
|
166
|
+
all_geq = False
|
|
167
|
+
elif va > vb:
|
|
168
|
+
any_better = True
|
|
169
|
+
else:
|
|
170
|
+
if va > vb:
|
|
171
|
+
all_geq = False
|
|
172
|
+
elif va < vb:
|
|
173
|
+
any_better = True
|
|
174
|
+
return all_geq and any_better
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def compute_pareto_front(
|
|
178
|
+
candidates: Sequence[Candidate[ConfigT]],
|
|
179
|
+
objectives: Sequence[ObjectiveSpec],
|
|
180
|
+
) -> List[Candidate[ConfigT]]:
|
|
181
|
+
"""Return the non-dominated subset of *candidates*.
|
|
182
|
+
|
|
183
|
+
Exact duplicates — same configuration identity and same measured
|
|
184
|
+
objectives — collapse to their first occurrence, so a configuration a
|
|
185
|
+
population re-visits across generations appears once on the front rather
|
|
186
|
+
than once per visit. A configuration re-evaluated to *different*
|
|
187
|
+
objectives is a genuinely different measurement and both rows remain;
|
|
188
|
+
dropping one silently would be the library's judgement, not the caller's.
|
|
189
|
+
"""
|
|
190
|
+
if not candidates:
|
|
191
|
+
return []
|
|
192
|
+
from agent_evolve.contract import artifact_key
|
|
193
|
+
|
|
194
|
+
unique: List[Candidate[ConfigT]] = []
|
|
195
|
+
seen: set = set()
|
|
196
|
+
for c in candidates:
|
|
197
|
+
key = (artifact_key(c.configuration), tuple(sorted(c.objectives.items())))
|
|
198
|
+
if key in seen:
|
|
199
|
+
continue
|
|
200
|
+
seen.add(key)
|
|
201
|
+
unique.append(c)
|
|
202
|
+
front: List[Candidate[ConfigT]] = []
|
|
203
|
+
for i, c in enumerate(unique):
|
|
204
|
+
if not any(
|
|
205
|
+
dominates(other.objectives, c.objectives, objectives)
|
|
206
|
+
for j, other in enumerate(unique)
|
|
207
|
+
if j != i
|
|
208
|
+
):
|
|
209
|
+
front.append(c)
|
|
210
|
+
return front
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ------------------------------------------------------------------
|
|
214
|
+
# Best-candidate selection
|
|
215
|
+
# ------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
def select_best_candidate(
|
|
218
|
+
pareto: Sequence[Candidate[ConfigT]],
|
|
219
|
+
objectives: Sequence[ObjectiveSpec],
|
|
220
|
+
priority_order: Optional[List[str]] = None,
|
|
221
|
+
) -> Optional[Candidate[ConfigT]]:
|
|
222
|
+
"""Lexicographic selection from the Pareto front.
|
|
223
|
+
|
|
224
|
+
Default priority: maximise objectives first, then minimise objectives.
|
|
225
|
+
"""
|
|
226
|
+
if not pareto:
|
|
227
|
+
return None
|
|
228
|
+
if priority_order is None:
|
|
229
|
+
max_objs = [s for s in objectives if s.goal == "max"]
|
|
230
|
+
min_objs = [s for s in objectives if s.goal == "min"]
|
|
231
|
+
priority_order = [s.name for s in max_objs] + [s.name for s in min_objs]
|
|
232
|
+
obj_map = {s.name: s for s in objectives}
|
|
233
|
+
unknown = [name for name in priority_order if name not in obj_map]
|
|
234
|
+
if unknown:
|
|
235
|
+
raise ProblemContractError(
|
|
236
|
+
"Unknown priority objective name(s): " + ", ".join(unknown)
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def _key(c: Candidate[ConfigT]) -> Tuple[float, ...]:
|
|
240
|
+
parts: List[float] = []
|
|
241
|
+
for name in priority_order:
|
|
242
|
+
spec = obj_map[name]
|
|
243
|
+
val = objective_value(c.objectives, name)
|
|
244
|
+
parts.append(-val if spec.goal == "max" else val)
|
|
245
|
+
return tuple(parts)
|
|
246
|
+
|
|
247
|
+
return min(pareto, key=_key)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# ------------------------------------------------------------------
|
|
251
|
+
# Minimax-rank selection
|
|
252
|
+
# ------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
def _rank_candidates(
|
|
255
|
+
candidates: Sequence[Candidate[ConfigT]],
|
|
256
|
+
objectives: Sequence[ObjectiveSpec],
|
|
257
|
+
) -> List[List[int]]:
|
|
258
|
+
"""``ranks[i][j]`` = 1-based **dense** rank of candidate *i* on objective *j* (1 = best).
|
|
259
|
+
|
|
260
|
+
Ties share the same rank; the next distinct value gets the next integer (no gaps
|
|
261
|
+
from skipped positions).
|
|
262
|
+
"""
|
|
263
|
+
n = len(candidates)
|
|
264
|
+
ranks: List[List[int]] = [[0] * len(objectives) for _ in range(n)]
|
|
265
|
+
for j, spec in enumerate(objectives):
|
|
266
|
+
values = [objective_value(c.objectives, spec.name) for c in candidates]
|
|
267
|
+
reverse = spec.goal == "max"
|
|
268
|
+
order = sorted(range(n), key=lambda i: values[i], reverse=reverse)
|
|
269
|
+
dense = 1
|
|
270
|
+
for pos, idx in enumerate(order):
|
|
271
|
+
if pos > 0 and values[order[pos]] != values[order[pos - 1]]:
|
|
272
|
+
dense += 1
|
|
273
|
+
ranks[idx][j] = dense
|
|
274
|
+
return ranks
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def select_minimax_rank(
|
|
278
|
+
candidates: Sequence[Candidate[ConfigT]],
|
|
279
|
+
objectives: Sequence[ObjectiveSpec],
|
|
280
|
+
) -> Optional[Candidate[ConfigT]]:
|
|
281
|
+
"""Best pick for multi-objective summaries: minimax over per-objective ranks.
|
|
282
|
+
|
|
283
|
+
For each candidate, compute **dense** rank on each objective (1 = best among
|
|
284
|
+
*candidates*). Take the **maximum** rank across objectives (bottleneck / worst
|
|
285
|
+
placement). Prefer the candidate(s) with the **smallest** bottleneck (minimax).
|
|
286
|
+
|
|
287
|
+
If several tie, pick the one with the **smallest sum of ranks** (more uniform
|
|
288
|
+
strength, not a spike on one metric).
|
|
289
|
+
"""
|
|
290
|
+
if not candidates:
|
|
291
|
+
return None
|
|
292
|
+
if len(candidates) == 1:
|
|
293
|
+
return candidates[0]
|
|
294
|
+
ranks = _rank_candidates(candidates, objectives)
|
|
295
|
+
worst = [max(r) for r in ranks]
|
|
296
|
+
min_worst = min(worst)
|
|
297
|
+
tied = [i for i in range(len(candidates)) if worst[i] == min_worst]
|
|
298
|
+
if len(tied) == 1:
|
|
299
|
+
return candidates[tied[0]]
|
|
300
|
+
best_idx = min(tied, key=lambda i: sum(ranks[i]))
|
|
301
|
+
return candidates[best_idx]
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def sort_by_minimax_rank(
|
|
305
|
+
candidates: Sequence[Candidate[ConfigT]],
|
|
306
|
+
objectives: Sequence[ObjectiveSpec],
|
|
307
|
+
) -> List[Candidate[ConfigT]]:
|
|
308
|
+
"""Order *candidates* by the same rule as :func:`select_minimax_rank`.
|
|
309
|
+
|
|
310
|
+
Primary key: smallest worst per-objective (dense) rank. Secondary: smallest
|
|
311
|
+
sum of per-objective ranks. The first element matches what
|
|
312
|
+
``select_minimax_rank(candidates, objectives)`` returns when not ``None``.
|
|
313
|
+
"""
|
|
314
|
+
if not candidates:
|
|
315
|
+
return []
|
|
316
|
+
if len(candidates) == 1:
|
|
317
|
+
return [candidates[0]]
|
|
318
|
+
ranks = _rank_candidates(candidates, objectives)
|
|
319
|
+
order = sorted(
|
|
320
|
+
range(len(candidates)),
|
|
321
|
+
key=lambda i: (max(ranks[i]), sum(ranks[i])),
|
|
322
|
+
)
|
|
323
|
+
return [candidates[i] for i in order]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Performance statistics and failure sampling for the insight prompts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
from typing import Any, Dict, List, Optional, Sequence
|
|
7
|
+
|
|
8
|
+
from agent_evolve.core.formatting import (
|
|
9
|
+
CandidateResult,
|
|
10
|
+
candidate_to_result,
|
|
11
|
+
result_to_candidate,
|
|
12
|
+
)
|
|
13
|
+
from agent_evolve.core.problem import ObjectiveSpec
|
|
14
|
+
from agent_evolve.core.results import compute_pareto_front, objective_value, sort_by_minimax_rank
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def compute_performance_stats(
|
|
18
|
+
valid_results: Sequence[CandidateResult],
|
|
19
|
+
objectives: Sequence[ObjectiveSpec],
|
|
20
|
+
) -> Optional[Dict[str, Any]]:
|
|
21
|
+
"""Compute best/worst per objective and the top Pareto candidates."""
|
|
22
|
+
if not valid_results:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
stats: Dict[str, Any] = {}
|
|
26
|
+
|
|
27
|
+
for spec in objectives:
|
|
28
|
+
key = spec.name
|
|
29
|
+
if spec.goal == "max":
|
|
30
|
+
best = max(valid_results, key=lambda r: objective_value(r.objectives, key))
|
|
31
|
+
worst = min(valid_results, key=lambda r: objective_value(r.objectives, key))
|
|
32
|
+
else:
|
|
33
|
+
best = min(valid_results, key=lambda r: objective_value(r.objectives, key))
|
|
34
|
+
worst = max(valid_results, key=lambda r: objective_value(r.objectives, key))
|
|
35
|
+
stats[f"best_{key}"] = best
|
|
36
|
+
stats[f"worst_{key}"] = worst
|
|
37
|
+
|
|
38
|
+
candidates = [result_to_candidate(r) for r in valid_results]
|
|
39
|
+
pareto_candidates = compute_pareto_front(candidates, objectives)
|
|
40
|
+
sorted_pareto = sort_by_minimax_rank(pareto_candidates, objectives)
|
|
41
|
+
pareto_results = [candidate_to_result(c) for c in sorted_pareto]
|
|
42
|
+
stats["top_3_pareto"] = pareto_results[:3]
|
|
43
|
+
stats["pareto_front"] = pareto_results
|
|
44
|
+
stats["pareto_size"] = len(pareto_results)
|
|
45
|
+
|
|
46
|
+
return stats
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def sample_failed_for_constraint(
|
|
50
|
+
latest_failed: Sequence[CandidateResult],
|
|
51
|
+
all_previous_failed: Sequence[CandidateResult],
|
|
52
|
+
max_examples: int,
|
|
53
|
+
rng: Optional[random.Random] = None,
|
|
54
|
+
) -> List[CandidateResult]:
|
|
55
|
+
"""Sample failures for constraint-instruction generation.
|
|
56
|
+
|
|
57
|
+
Always includes the latest failures; fills remaining slots with random
|
|
58
|
+
previous failures using the injected ``rng`` for reproducibility.
|
|
59
|
+
"""
|
|
60
|
+
sampled = list(latest_failed)
|
|
61
|
+
if len(sampled) >= max_examples:
|
|
62
|
+
return sampled[:max_examples]
|
|
63
|
+
|
|
64
|
+
remaining = max_examples - len(sampled)
|
|
65
|
+
latest_ids = {id(r) for r in latest_failed}
|
|
66
|
+
previous = [r for r in all_previous_failed if id(r) not in latest_ids]
|
|
67
|
+
if previous and remaining > 0:
|
|
68
|
+
picker = rng or random
|
|
69
|
+
sampled.extend(picker.sample(previous, min(remaining, len(previous))))
|
|
70
|
+
return sampled
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""What each guidance mechanism actually did, surfaced with the result.
|
|
2
|
+
|
|
3
|
+
The seam objects count their own behaviour (calls, rejections, authoring
|
|
4
|
+
attempts) and always have — but the counters lived as attributes on closures
|
|
5
|
+
the caller discarded, so no run could report what its guidance did. A number
|
|
6
|
+
that is counted but unreachable might as well not exist: the difference
|
|
7
|
+
between "guidance did not help" and "guidance never arrived" is exactly these
|
|
8
|
+
counters, and it must be readable off the ``SearchResult``.
|
|
9
|
+
|
|
10
|
+
The contract is deliberately small. Any seam object may carry:
|
|
11
|
+
|
|
12
|
+
``telemetry`` an object whose ``as_dict()`` returns integer counters;
|
|
13
|
+
``mechanism`` a short name for what the seam decides (``"chooser"``);
|
|
14
|
+
``authored_by`` who authored the decisions — ``"llm"``, ``"rule"``, or
|
|
15
|
+
``"none"``.
|
|
16
|
+
|
|
17
|
+
:func:`harvest_telemetry` gathers whatever is present and skips whatever is
|
|
18
|
+
not, so the unguided path reports an empty mechanism list rather than nothing
|
|
19
|
+
at all — a measured zero, distinguishable from "nobody looked".
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from typing import Any, Iterable, Mapping, Optional, Tuple
|
|
26
|
+
|
|
27
|
+
__all__ = ["MechanismTelemetry", "RunTelemetry", "harvest_telemetry"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class MechanismTelemetry:
|
|
32
|
+
"""One mechanism's counters, labelled with who authored its decisions."""
|
|
33
|
+
|
|
34
|
+
mechanism: str
|
|
35
|
+
authored_by: str
|
|
36
|
+
counters: Mapping[str, int]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class RunTelemetry:
|
|
41
|
+
"""A run's mechanism counters plus its evaluation ledger.
|
|
42
|
+
|
|
43
|
+
``real_evaluations`` counts evaluator invocations that were charged
|
|
44
|
+
against the budget. ``virtual_evaluations`` counts surrogate predictions —
|
|
45
|
+
free by construction, and reported separately precisely so the two can
|
|
46
|
+
never be conflated in a budget claim.
|
|
47
|
+
|
|
48
|
+
``proxy_evaluations`` counts calls to a problem's CHEAPER evaluation
|
|
49
|
+
fidelity (``Problem.evaluate_proxy``). They are not free — they burn real
|
|
50
|
+
evaluator seconds — and they are not charged: the budget a claim is
|
|
51
|
+
denominated in counts full-fidelity evaluations. A campaign that spends
|
|
52
|
+
them must therefore report them, which is why they have a field of their
|
|
53
|
+
own here rather than a line in someone's log.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
mechanisms: Tuple[MechanismTelemetry, ...] = ()
|
|
57
|
+
real_evaluations: int = 0
|
|
58
|
+
virtual_evaluations: int = 0
|
|
59
|
+
proxy_evaluations: int = 0
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def harvest_telemetry(
|
|
63
|
+
sources: Iterable[Optional[Any]],
|
|
64
|
+
*,
|
|
65
|
+
real_evaluations: int = 0,
|
|
66
|
+
virtual_evaluations: int = 0,
|
|
67
|
+
proxy_evaluations: int = 0,
|
|
68
|
+
) -> RunTelemetry:
|
|
69
|
+
"""Collect telemetry from whichever *sources* carry it.
|
|
70
|
+
|
|
71
|
+
``None`` entries and objects without a usable ``telemetry.as_dict()`` are
|
|
72
|
+
skipped silently: absence of counters is a legitimate state (the random
|
|
73
|
+
chooser, the statistical prior), not an error.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
mechanisms = []
|
|
77
|
+
for source in sources:
|
|
78
|
+
if source is None:
|
|
79
|
+
continue
|
|
80
|
+
counter = getattr(source, "telemetry", None)
|
|
81
|
+
as_dict = getattr(counter, "as_dict", None)
|
|
82
|
+
if not callable(as_dict):
|
|
83
|
+
continue
|
|
84
|
+
counters = {str(k): int(v) for k, v in dict(as_dict()).items()}
|
|
85
|
+
mechanisms.append(
|
|
86
|
+
MechanismTelemetry(
|
|
87
|
+
mechanism=str(
|
|
88
|
+
getattr(source, "mechanism", None)
|
|
89
|
+
or getattr(source, "__name__", type(source).__name__)
|
|
90
|
+
),
|
|
91
|
+
authored_by=str(getattr(source, "authored_by", "unrecorded")),
|
|
92
|
+
counters=counters,
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
return RunTelemetry(
|
|
96
|
+
mechanisms=tuple(mechanisms),
|
|
97
|
+
real_evaluations=int(real_evaluations),
|
|
98
|
+
virtual_evaluations=int(virtual_evaluations),
|
|
99
|
+
proxy_evaluations=int(proxy_evaluations),
|
|
100
|
+
)
|