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,704 @@
|
|
|
1
|
+
"""What the run MEASURED, rendered so a model can reason about it.
|
|
2
|
+
|
|
3
|
+
Every authored mechanism in this package so far has been conditioned on
|
|
4
|
+
MEANING: the model reads a semantic card and a schema and writes an artifact
|
|
5
|
+
before a single evaluation exists. That is a static prior. It is worth a great
|
|
6
|
+
deal where a recallable domain prior is strong and it is worth exactly nothing
|
|
7
|
+
where one is not, and no amount of re-prompting changes which of those two a
|
|
8
|
+
venue is -- because the prompt never contains a measurement.
|
|
9
|
+
|
|
10
|
+
This module is the other half: it turns the run's own measured
|
|
11
|
+
``(configuration -> objectives)`` trace into evidence a model can be asked to
|
|
12
|
+
reason over. It is deliberately a pure function of the trace:
|
|
13
|
+
|
|
14
|
+
- it never sees the problem, the evaluator, the cache or the budget, so
|
|
15
|
+
rendering evidence cannot spend one;
|
|
16
|
+
- what it emits is TEXT plus a digest of that text, so a run can record
|
|
17
|
+
exactly what the model was shown and no run can imply it reasoned over
|
|
18
|
+
measurements when it did not;
|
|
19
|
+
- it computes nothing a reader cannot recompute from the same rows.
|
|
20
|
+
|
|
21
|
+
Three things are rendered, in the order the evidence supports them:
|
|
22
|
+
|
|
23
|
+
``front`` the non-dominated members measured so far -- the run's own
|
|
24
|
+
weight-free verdict on what is good, the same relation
|
|
25
|
+
selection already uses.
|
|
26
|
+
``progress`` per objective, the best value in the first half of the trace
|
|
27
|
+
against the best in the second, so "what improved and what
|
|
28
|
+
did not" is a measurement and not an adjective.
|
|
29
|
+
``effects`` per (locus, objective) rank correlation over the measured
|
|
30
|
+
rows: WHICH KNOB MOVES WHICH COST. This is the term a genetic
|
|
31
|
+
algorithm can only discover by stumbling and a reader of
|
|
32
|
+
twenty measured points should be able to name.
|
|
33
|
+
|
|
34
|
+
``coverage`` rides along with ``effects``: which declared values a locus has
|
|
35
|
+
actually been measured at, so "this region is exhausted" is checkable.
|
|
36
|
+
|
|
37
|
+
:func:`render_elite_table` renders a fourth thing separately, because it is a
|
|
38
|
+
different KIND of statement and its consumers ask for it by name: value
|
|
39
|
+
occupancy among the non-dominated rows, against occupancy across the whole
|
|
40
|
+
trace. ``effects`` needs rows before it says anything true; occupancy is a
|
|
41
|
+
count over configurations and survives the tens of rows a mid-run reader
|
|
42
|
+
actually has. Its docstring carries the measurement that settled which of the
|
|
43
|
+
two to lead with.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
import hashlib
|
|
49
|
+
import json
|
|
50
|
+
import math
|
|
51
|
+
from dataclasses import dataclass
|
|
52
|
+
from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple
|
|
53
|
+
|
|
54
|
+
from agent_evolve.core.problem import ObjectiveSpec
|
|
55
|
+
from agent_evolve.core.results import dominates
|
|
56
|
+
from agent_evolve.policies.genetic import Locus, loci_of, read_locus
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"MIN_EVIDENCE_ROWS",
|
|
60
|
+
"ELITE_ENRICHMENT_FLOOR",
|
|
61
|
+
"ELITE_MIN_COUNT",
|
|
62
|
+
"MeasuredRow",
|
|
63
|
+
"EvidenceView",
|
|
64
|
+
"front_of",
|
|
65
|
+
"spearman",
|
|
66
|
+
"locus_effects",
|
|
67
|
+
"render_measurement_evidence",
|
|
68
|
+
"render_elite_table",
|
|
69
|
+
"evidence_digest",
|
|
70
|
+
"WeightedProposal",
|
|
71
|
+
"PriorVerdict",
|
|
72
|
+
"parse_weighted_restriction",
|
|
73
|
+
"admit_weighted_restriction",
|
|
74
|
+
"apply_weighted_restriction",
|
|
75
|
+
"WEIGHTED_RESTRICTION_PROMPT",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
#: One measured candidate: its configuration, what the evaluator returned,
|
|
79
|
+
#: and whether it survived selection. Survival is the run's own weight-free
|
|
80
|
+
#: quality verdict and costs nothing extra to carry.
|
|
81
|
+
MeasuredRow = Tuple[Mapping[str, Any], Mapping[str, float], bool]
|
|
82
|
+
|
|
83
|
+
#: The fewest measured rows from which this module can render ONE determinable
|
|
84
|
+
#: statement about which knob moves which cost -- ``spearman`` is undefined
|
|
85
|
+
#: below three pairs, so evidence built from fewer rows carries a front and a
|
|
86
|
+
#: progress line and not a single ``effects`` entry, which is the term the
|
|
87
|
+
#: locus-importance channel exists to act on. It is therefore the honest floor
|
|
88
|
+
#: for "the evidence gate can be met", and it is exported so a consumer states
|
|
89
|
+
#: its threshold in terms of what the evidence can actually support rather
|
|
90
|
+
#: than picking a number.
|
|
91
|
+
MIN_EVIDENCE_ROWS = 3
|
|
92
|
+
|
|
93
|
+
#: The seam a CONTROL arm wraps. A view receives the rows this run measured
|
|
94
|
+
#: and returns the rows the model will be shown. The identity view is the
|
|
95
|
+
#: product; a view that returns another run's rows -- same count, same shape,
|
|
96
|
+
#: same cost, wrong run -- is the shuffled-evidence control that says whether
|
|
97
|
+
#: any measured gain comes from reasoning over THIS run's evidence or merely
|
|
98
|
+
#: from making another call. It lives here, as a declared parameter, because a
|
|
99
|
+
#: mechanism whose control cannot be built without editing the product is a
|
|
100
|
+
#: mechanism whose control will not be built.
|
|
101
|
+
EvidenceView = Callable[[Sequence[MeasuredRow]], Sequence[MeasuredRow]]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def front_of(
|
|
105
|
+
rows: Sequence[MeasuredRow],
|
|
106
|
+
specs: Sequence[ObjectiveSpec],
|
|
107
|
+
) -> List[MeasuredRow]:
|
|
108
|
+
"""The non-dominated subset of *rows*, in measurement order."""
|
|
109
|
+
|
|
110
|
+
kept: List[MeasuredRow] = []
|
|
111
|
+
for row in rows:
|
|
112
|
+
if any(dominates(dict(other[1]), dict(row[1]), specs)
|
|
113
|
+
for other in rows if other is not row):
|
|
114
|
+
continue
|
|
115
|
+
kept.append(row)
|
|
116
|
+
return kept
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _ranks(values: Sequence[float]) -> List[float]:
|
|
120
|
+
"""Average ranks, so ties do not manufacture correlation."""
|
|
121
|
+
|
|
122
|
+
order = sorted(range(len(values)), key=lambda i: values[i])
|
|
123
|
+
out = [0.0] * len(values)
|
|
124
|
+
i = 0
|
|
125
|
+
while i < len(order):
|
|
126
|
+
j = i
|
|
127
|
+
while j + 1 < len(order) and values[order[j + 1]] == values[order[i]]:
|
|
128
|
+
j += 1
|
|
129
|
+
mean_rank = (i + j) / 2.0 + 1.0
|
|
130
|
+
for k in range(i, j + 1):
|
|
131
|
+
out[order[k]] = mean_rank
|
|
132
|
+
i = j + 1
|
|
133
|
+
return out
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def spearman(xs: Sequence[float], ys: Sequence[float]) -> Optional[float]:
|
|
137
|
+
"""Rank correlation, or ``None`` when it is not defined.
|
|
138
|
+
|
|
139
|
+
Undefined means undefined and is reported as such: fewer than three
|
|
140
|
+
pairs, or a constant column, cannot produce a correlation and must not
|
|
141
|
+
produce a zero that reads like "measured, no effect".
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
if len(xs) != len(ys) or len(xs) < 3:
|
|
145
|
+
return None
|
|
146
|
+
if len(set(xs)) < 2 or len(set(ys)) < 2:
|
|
147
|
+
return None
|
|
148
|
+
rx, ry = _ranks(list(xs)), _ranks(list(ys))
|
|
149
|
+
mx = sum(rx) / len(rx)
|
|
150
|
+
my = sum(ry) / len(ry)
|
|
151
|
+
num = sum((a - mx) * (b - my) for a, b in zip(rx, ry))
|
|
152
|
+
den = math.sqrt(sum((a - mx) ** 2 for a in rx)
|
|
153
|
+
* sum((b - my) ** 2 for b in ry))
|
|
154
|
+
if den <= 0.0:
|
|
155
|
+
return None
|
|
156
|
+
return max(-1.0, min(1.0, num / den))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass(frozen=True)
|
|
160
|
+
class LocusEffect:
|
|
161
|
+
"""One locus, and what the trace says it does to each objective."""
|
|
162
|
+
|
|
163
|
+
locus: str
|
|
164
|
+
#: objective name -> rank correlation between the locus's position in its
|
|
165
|
+
#: DECLARED value order and that objective. ``None`` where undefined.
|
|
166
|
+
correlation: Mapping[str, Optional[float]]
|
|
167
|
+
#: declared value -> how many measured rows hold it.
|
|
168
|
+
coverage: Mapping[str, int]
|
|
169
|
+
unmeasured: Tuple[str, ...]
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def strength(self) -> float:
|
|
173
|
+
return max((abs(v) for v in self.correlation.values() if v is not None),
|
|
174
|
+
default=0.0)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def locus_effects(
|
|
178
|
+
rows: Sequence[MeasuredRow],
|
|
179
|
+
specs: Sequence[ObjectiveSpec],
|
|
180
|
+
domains: Mapping[str, Sequence[Any]],
|
|
181
|
+
) -> List[LocusEffect]:
|
|
182
|
+
"""Per-locus rank correlations and coverage, strongest effect first.
|
|
183
|
+
|
|
184
|
+
The predictor is a locus's POSITION IN ITS DECLARED VALUE ORDER, not its
|
|
185
|
+
value: the declared order is the only ordering this package is allowed to
|
|
186
|
+
assume about a categorical domain, it is what the schema published, and it
|
|
187
|
+
is the same order the sampler draws from. Where a domain is genuinely
|
|
188
|
+
unordered the correlation is meaningless and the model is told the
|
|
189
|
+
coverage instead -- which is why both travel together.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
if not rows:
|
|
193
|
+
return []
|
|
194
|
+
template = dict(rows[0][0])
|
|
195
|
+
effects: List[LocusEffect] = []
|
|
196
|
+
for locus in loci_of(template):
|
|
197
|
+
key = str(locus)
|
|
198
|
+
domain = list(domains.get(key) or ())
|
|
199
|
+
if len(domain) < 2:
|
|
200
|
+
continue
|
|
201
|
+
index_of = {_token(v): i for i, v in enumerate(domain)}
|
|
202
|
+
positions: List[float] = []
|
|
203
|
+
keep: List[int] = []
|
|
204
|
+
counts: Dict[str, int] = {_token(v): 0 for v in domain}
|
|
205
|
+
for i, (config, _obj, _s) in enumerate(rows):
|
|
206
|
+
try:
|
|
207
|
+
token = _token(read_locus(config, locus))
|
|
208
|
+
except Exception:
|
|
209
|
+
continue
|
|
210
|
+
if token not in index_of:
|
|
211
|
+
continue
|
|
212
|
+
counts[token] += 1
|
|
213
|
+
positions.append(float(index_of[token]))
|
|
214
|
+
keep.append(i)
|
|
215
|
+
correlation: Dict[str, Optional[float]] = {}
|
|
216
|
+
for spec in specs:
|
|
217
|
+
column = [float(rows[i][1].get(spec.name, 0.0)) for i in keep]
|
|
218
|
+
correlation[spec.name] = spearman(positions, column)
|
|
219
|
+
effects.append(LocusEffect(
|
|
220
|
+
locus=key,
|
|
221
|
+
correlation=correlation,
|
|
222
|
+
coverage=dict(counts),
|
|
223
|
+
unmeasured=tuple(t for t, c in counts.items() if c == 0),
|
|
224
|
+
))
|
|
225
|
+
effects.sort(key=lambda e: (-e.strength, e.locus))
|
|
226
|
+
return effects
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _token(value: Any) -> str:
|
|
230
|
+
return value if isinstance(value, str) else json.dumps(value, default=str)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _fmt(value: float) -> str:
|
|
234
|
+
return f"{float(value):.6g}"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def render_measurement_evidence(
|
|
238
|
+
rows: Sequence[MeasuredRow],
|
|
239
|
+
specs: Sequence[ObjectiveSpec],
|
|
240
|
+
domains: Mapping[str, Sequence[Any]],
|
|
241
|
+
*,
|
|
242
|
+
front_shown: int = 8,
|
|
243
|
+
effects_shown: int = 8,
|
|
244
|
+
charged: Optional[int] = None,
|
|
245
|
+
) -> str:
|
|
246
|
+
"""The measured trace as prompt text. Pure, and a pure function of *rows*.
|
|
247
|
+
|
|
248
|
+
Nothing here is an opinion. Every line is a count, a measured value or a
|
|
249
|
+
rank correlation over the rows passed in, so the same rows always render
|
|
250
|
+
the same bytes and :func:`evidence_digest` of that text identifies the
|
|
251
|
+
evidence a call was conditioned on.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
rows = list(rows)
|
|
255
|
+
if not rows:
|
|
256
|
+
return " no candidate has been measured yet."
|
|
257
|
+
lines: List[str] = [
|
|
258
|
+
f" measured so far: {len(rows)} configurations"
|
|
259
|
+
+ (f" ({charged} charged evaluations)" if charged is not None else ""),
|
|
260
|
+
]
|
|
261
|
+
|
|
262
|
+
front = front_of(rows, specs)
|
|
263
|
+
lines.append(f" the current non-dominated front ({len(front)} of "
|
|
264
|
+
f"{len(rows)} measured):")
|
|
265
|
+
for config, objectives, _s in front[:front_shown]:
|
|
266
|
+
rendered = ", ".join(f"{s.name}={_fmt(objectives.get(s.name, 0.0))}"
|
|
267
|
+
for s in specs)
|
|
268
|
+
lines.append(f" {json.dumps(config, sort_keys=True, default=str)}"
|
|
269
|
+
f" -> {rendered}")
|
|
270
|
+
if len(front) > front_shown:
|
|
271
|
+
lines.append(f" ... and {len(front) - front_shown} more front members")
|
|
272
|
+
|
|
273
|
+
lines.append(" progress -- best value per objective, first half of the "
|
|
274
|
+
"run vs second half:")
|
|
275
|
+
half = max(1, len(rows) // 2)
|
|
276
|
+
for spec in specs:
|
|
277
|
+
early = [float(o.get(spec.name, 0.0)) for _c, o, _s in rows[:half]]
|
|
278
|
+
late = [float(o.get(spec.name, 0.0)) for _c, o, _s in rows[half:]]
|
|
279
|
+
pick = min if spec.goal == "min" else max
|
|
280
|
+
best_early = pick(early) if early else float("nan")
|
|
281
|
+
best_late = pick(late) if late else best_early
|
|
282
|
+
moved = ("IMPROVED" if late and pick([best_early, best_late]) == best_late
|
|
283
|
+
and best_late != best_early else "did not improve")
|
|
284
|
+
lines.append(f" {spec.name} ({spec.goal}imise): {_fmt(best_early)} "
|
|
285
|
+
f"-> {_fmt(best_late)} [{moved}]")
|
|
286
|
+
|
|
287
|
+
effects = locus_effects(rows, specs, domains)
|
|
288
|
+
if effects:
|
|
289
|
+
lines.append(" which parameter moves which cost -- rank correlation "
|
|
290
|
+
"between the parameter's position in its declared value "
|
|
291
|
+
"list and the measured cost (+1 = later values cost more, "
|
|
292
|
+
"-1 = later values cost less, '-' = not determinable "
|
|
293
|
+
"from these rows):")
|
|
294
|
+
for effect in effects[:effects_shown]:
|
|
295
|
+
body = ", ".join(
|
|
296
|
+
f"{name}={'-' if value is None else f'{value:+.2f}'}"
|
|
297
|
+
for name, value in effect.correlation.items())
|
|
298
|
+
unmeasured = (f"; never measured at: "
|
|
299
|
+
f"{', '.join(effect.unmeasured)}"
|
|
300
|
+
if effect.unmeasured else "; every declared value "
|
|
301
|
+
"has been measured")
|
|
302
|
+
lines.append(f" {effect.locus}: {body}{unmeasured}")
|
|
303
|
+
weak = [e.locus for e in effects[effects_shown:] if e.strength < 0.2]
|
|
304
|
+
if weak:
|
|
305
|
+
lines.append(f" (no determinable effect on any cost: "
|
|
306
|
+
f"{', '.join(weak[:12])})")
|
|
307
|
+
return "\n".join(lines)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
#: How far a value's share among the front must exceed its share of the whole
|
|
311
|
+
#: trace before this table calls it informative. At the tens of rows a mid-run
|
|
312
|
+
#: revision actually has, a smaller gap is one row changing its mind.
|
|
313
|
+
ELITE_ENRICHMENT_FLOOR = 0.05
|
|
314
|
+
|
|
315
|
+
#: How often a value must appear among the front to be worth printing whatever
|
|
316
|
+
#: its enrichment. Twice is the fewest that is not a single draw.
|
|
317
|
+
ELITE_MIN_COUNT = 2
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def render_elite_table(
|
|
321
|
+
rows: Sequence[MeasuredRow],
|
|
322
|
+
specs: Sequence[ObjectiveSpec],
|
|
323
|
+
domains: Mapping[str, Sequence[Any]],
|
|
324
|
+
*,
|
|
325
|
+
max_elite: int = 12,
|
|
326
|
+
) -> str:
|
|
327
|
+
"""What the non-dominated configurations are MADE OF. Pure, like the rest.
|
|
328
|
+
|
|
329
|
+
``effects`` above answers "which knob moves which cost" with a per-locus
|
|
330
|
+
rank correlation, and that answer has a measured floor under it: it needs
|
|
331
|
+
rows. A mid-run revision has 40-90 of them spread over a two-dozen-field
|
|
332
|
+
space, and at that scale the correlations are noise -- the W1 pilot's
|
|
333
|
+
revision channel re-tilted them four times a run and moved the final result
|
|
334
|
+
by less than the revision draw's own sampling variance (2026-08-19, five
|
|
335
|
+
taped analog pairs: 2/5 by sign under both bet variants, and the same arm
|
|
336
|
+
on the same tape moving 0.29 between two draws). Re-reading a noisy
|
|
337
|
+
statistic more often does not make it a signal.
|
|
338
|
+
|
|
339
|
+
What DID separate, sealed, was a graded prior whose author never computed a
|
|
340
|
+
correlation: the format that seat worked in was value OCCUPANCY -- which
|
|
341
|
+
declared values the configurations that are already good are built out of,
|
|
342
|
+
read against how often the same value shows up in the run at large. It is a
|
|
343
|
+
count over configurations rather than a statistic over ranks, it assumes no
|
|
344
|
+
ordering on a categorical domain, and one more row cannot swing it the way
|
|
345
|
+
one more row swings a Spearman. This function renders that format.
|
|
346
|
+
|
|
347
|
+
The elite are the goal-aware rank-0 rows -- :func:`front_of`, the same
|
|
348
|
+
domination relation selection itself uses -- truncated to *max_elite* in
|
|
349
|
+
measurement order when the front is larger, so a run whose front is half
|
|
350
|
+
its trace still gets a table a model can read.
|
|
351
|
+
|
|
352
|
+
Occupancy is per FIELD, not per locus: a sequence field's elements pool
|
|
353
|
+
into one entry, because the weight table a revision writes is keyed per
|
|
354
|
+
field and a table per position would be a different prior for every genome
|
|
355
|
+
length. Pooling makes the denominator the number of in-domain SLOTS the
|
|
356
|
+
field contributes rather than the number of rows, which for a scalar field
|
|
357
|
+
is exactly the number of rows and so reduces to "count among elite / elite
|
|
358
|
+
size".
|
|
359
|
+
|
|
360
|
+
Only values worth the line are printed: a value whose elite share beats its
|
|
361
|
+
overall share by more than :data:`ELITE_ENRICHMENT_FLOOR`, or that the
|
|
362
|
+
front holds at least :data:`ELITE_MIN_COUNT` times. A field with no such
|
|
363
|
+
value is omitted entirely and the closing line counts how many fields that
|
|
364
|
+
was, so an omission reads as "measured, said nothing" rather than as an
|
|
365
|
+
absence the reader has to notice.
|
|
366
|
+
|
|
367
|
+
NO objective value appears anywhere in this rendering. The front's scores
|
|
368
|
+
are the business of the section above; this one says only where the front
|
|
369
|
+
sits in the search space, so occupancy cannot be misread as a score.
|
|
370
|
+
"""
|
|
371
|
+
|
|
372
|
+
rows = list(rows)
|
|
373
|
+
if not rows:
|
|
374
|
+
return " no candidate has been measured yet."
|
|
375
|
+
|
|
376
|
+
fields: List[str] = []
|
|
377
|
+
declared: Dict[str, List[str]] = {}
|
|
378
|
+
for locus in loci_of(dict(rows[0][0])):
|
|
379
|
+
if locus.field in declared:
|
|
380
|
+
continue
|
|
381
|
+
values = list(domains.get(str(locus)) or ())
|
|
382
|
+
if len(values) < 2:
|
|
383
|
+
continue
|
|
384
|
+
fields.append(locus.field)
|
|
385
|
+
declared[locus.field] = [_token(v) for v in values]
|
|
386
|
+
|
|
387
|
+
def _tally(subset: Sequence[MeasuredRow]
|
|
388
|
+
) -> Tuple[Dict[str, Dict[str, int]], Dict[str, int]]:
|
|
389
|
+
counts = {name: {token: 0 for token in declared[name]}
|
|
390
|
+
for name in fields}
|
|
391
|
+
slots = {name: 0 for name in fields}
|
|
392
|
+
for config, _objectives, _survived in subset:
|
|
393
|
+
for locus in loci_of(dict(config)):
|
|
394
|
+
held = counts.get(locus.field)
|
|
395
|
+
if held is None:
|
|
396
|
+
continue
|
|
397
|
+
try:
|
|
398
|
+
token = _token(read_locus(config, locus))
|
|
399
|
+
except Exception:
|
|
400
|
+
continue
|
|
401
|
+
if token not in held:
|
|
402
|
+
continue
|
|
403
|
+
held[token] += 1
|
|
404
|
+
slots[locus.field] += 1
|
|
405
|
+
return counts, slots
|
|
406
|
+
|
|
407
|
+
front = front_of(rows, specs)
|
|
408
|
+
elite = front[:max_elite]
|
|
409
|
+
elite_counts, elite_slots = _tally(elite)
|
|
410
|
+
all_counts, all_slots = _tally(rows)
|
|
411
|
+
|
|
412
|
+
scope = (f"the {len(elite)} non-dominated configurations"
|
|
413
|
+
if len(front) <= len(elite) else
|
|
414
|
+
f"the first {len(elite)} of {len(front)} non-dominated "
|
|
415
|
+
f"configurations, in measurement order")
|
|
416
|
+
lines: List[str] = [
|
|
417
|
+
f" which declared values {scope} hold, as a share of that set, "
|
|
418
|
+
f"against the same value's share across all {len(rows)} measured "
|
|
419
|
+
f"configurations. These are counts of configurations, never scores:"
|
|
420
|
+
]
|
|
421
|
+
|
|
422
|
+
silent = 0
|
|
423
|
+
for name in fields:
|
|
424
|
+
shown: List[str] = []
|
|
425
|
+
for token in declared[name]:
|
|
426
|
+
elite_count = elite_counts[name][token]
|
|
427
|
+
elite_total = elite_slots[name]
|
|
428
|
+
elite_share = elite_count / elite_total if elite_total else 0.0
|
|
429
|
+
overall_total = all_slots[name]
|
|
430
|
+
overall_share = (all_counts[name][token] / overall_total
|
|
431
|
+
if overall_total else 0.0)
|
|
432
|
+
if not (elite_share - overall_share > ELITE_ENRICHMENT_FLOOR
|
|
433
|
+
or elite_count >= ELITE_MIN_COUNT):
|
|
434
|
+
continue
|
|
435
|
+
shown.append(f"{token} {elite_count}/{elite_total}="
|
|
436
|
+
f"{elite_share:.2f} vs {overall_share:.2f} overall "
|
|
437
|
+
f"({elite_share - overall_share:+.2f})")
|
|
438
|
+
if not shown:
|
|
439
|
+
silent += 1
|
|
440
|
+
continue
|
|
441
|
+
lines.append(f" {name}: " + "; ".join(shown))
|
|
442
|
+
lines.append(f" ({silent} of {len(fields)} parameters showed no value "
|
|
443
|
+
f"the front concentrates on)")
|
|
444
|
+
return "\n".join(lines)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def evidence_digest(text: str) -> str:
|
|
448
|
+
"""The identity of one evidence rendering. Recorded on every call."""
|
|
449
|
+
|
|
450
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
# ================================================ the locus-importance channel
|
|
455
|
+
|
|
456
|
+
WEIGHTED_RESTRICTION_PROMPT = """You are advising a black-box multi-objective \
|
|
457
|
+
optimizer about WHERE IN ITS SEARCH SPACE to concentrate the rest of its
|
|
458
|
+
evaluations.
|
|
459
|
+
|
|
460
|
+
OBJECTIVES (name and direction):
|
|
461
|
+
{goals}
|
|
462
|
+
|
|
463
|
+
SEARCH SPACE -- every parameter and the values it may take:
|
|
464
|
+
{domains}
|
|
465
|
+
|
|
466
|
+
WHAT THE OPTIMIZER HAS ACTUALLY MEASURED SO FAR:
|
|
467
|
+
|
|
468
|
+
{evidence}
|
|
469
|
+
|
|
470
|
+
Read the measurements, not the parameter names. Decide which parameters
|
|
471
|
+
actually move the measured costs, and for those parameters how much of the
|
|
472
|
+
remaining budget each value deserves.
|
|
473
|
+
|
|
474
|
+
Reply with ONLY a JSON object, no prose and no code fence, of this shape:
|
|
475
|
+
|
|
476
|
+
{{"weight": {{"<parameter>": {{"<value>": <positive number>, ...}}, ...}},
|
|
477
|
+
"because": {{"<parameter>": "<one short sentence citing the measurements>"}}}}
|
|
478
|
+
|
|
479
|
+
Rules, and the harness checks every one of them:
|
|
480
|
+
- Name ONLY parameters that appear in the search space above, and ONLY values
|
|
481
|
+
that parameter declares. Anything else and the whole reply is REFUSED.
|
|
482
|
+
- Weights BIAS sampling; they exclude nothing. A value you do not mention
|
|
483
|
+
keeps weight 1. Every declared value stays reachable no matter what you
|
|
484
|
+
reply, so you cannot lose the optimum -- but you can waste budget, so
|
|
485
|
+
weight only what the measurements justify.
|
|
486
|
+
- Weights must be positive numbers, and within one parameter the heaviest
|
|
487
|
+
value may outweigh the lightest by at most {max_ratio}x (unmentioned values
|
|
488
|
+
count as weight 1). More concentration than that and the whole reply is
|
|
489
|
+
REFUSED: concentration is the point; a de-facto exclusion is not.
|
|
490
|
+
- Leave "weight" empty if the measurements do not justify biasing anything.
|
|
491
|
+
An honest empty answer is accepted and costs nothing."""
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@dataclass(frozen=True)
|
|
495
|
+
class WeightedProposal:
|
|
496
|
+
"""The model's reply, typed and UNJUDGED: a weight table and its reasons.
|
|
497
|
+
|
|
498
|
+
This is the parse product, not the prior. The gate judges it, and only an
|
|
499
|
+
ADMITTED proposal becomes a
|
|
500
|
+
:class:`~agent_evolve.policies.weighted_prior.WeightedRestriction` -- the
|
|
501
|
+
package's one generic graded-prior form -- constructed over the FULL
|
|
502
|
+
declared domains with strictly positive weights, so it excludes nothing
|
|
503
|
+
by construction.
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
#: parameter -> {value token -> weight}, exactly as replied.
|
|
507
|
+
weight: Mapping[str, Mapping[str, float]] = ()
|
|
508
|
+
because: Mapping[str, str] = ()
|
|
509
|
+
|
|
510
|
+
def as_note(self) -> Dict[str, Any]:
|
|
511
|
+
return {"weight": {k: {t: float(w) for t, w in dict(v).items()}
|
|
512
|
+
for k, v in dict(self.weight).items()},
|
|
513
|
+
"because": dict(self.because)}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
@dataclass(frozen=True)
|
|
517
|
+
class PriorVerdict:
|
|
518
|
+
"""Admitted or refused, and WHY. The gate refuses; it never trusts."""
|
|
519
|
+
|
|
520
|
+
admitted: bool
|
|
521
|
+
reason: str = ""
|
|
522
|
+
proposal: Optional[WeightedProposal] = None
|
|
523
|
+
#: The admitted prior: the generic graded form, over the full declared
|
|
524
|
+
#: domains, every weight strictly positive. ``None`` unless admitted.
|
|
525
|
+
prior: Optional["WeightedRestriction"] = None
|
|
526
|
+
#: The largest max/min weight ratio any one parameter carries, implicit
|
|
527
|
+
#: weights included. ``1.0`` is "biases nothing".
|
|
528
|
+
concentration: float = 1.0
|
|
529
|
+
|
|
530
|
+
def as_note(self) -> Dict[str, Any]:
|
|
531
|
+
note: Dict[str, Any] = {"admitted": bool(self.admitted),
|
|
532
|
+
"reason": self.reason,
|
|
533
|
+
"concentration": round(self.concentration, 4)}
|
|
534
|
+
if self.proposal is not None:
|
|
535
|
+
note.update(self.proposal.as_note())
|
|
536
|
+
return note
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def parse_weighted_restriction(text: str) -> Optional[WeightedProposal]:
|
|
540
|
+
"""The model's reply as a typed proposal, or ``None`` if it is not one.
|
|
541
|
+
|
|
542
|
+
Whole-reply acceptance, exactly as the authored-artifact gate does it: a
|
|
543
|
+
reply that is not a JSON object of the declared shape -- weights as
|
|
544
|
+
numbers, per parameter, per value -- is not repaired into one, because a
|
|
545
|
+
repaired prior is a prior nobody authored.
|
|
546
|
+
"""
|
|
547
|
+
|
|
548
|
+
if not isinstance(text, str):
|
|
549
|
+
return None
|
|
550
|
+
body = text.strip()
|
|
551
|
+
if body.startswith("```"):
|
|
552
|
+
body = body.split("```")[1] if body.count("```") >= 2 else body
|
|
553
|
+
if body.startswith("json"):
|
|
554
|
+
body = body[4:]
|
|
555
|
+
start, end = body.find("{"), body.rfind("}")
|
|
556
|
+
if start < 0 or end <= start:
|
|
557
|
+
return None
|
|
558
|
+
try:
|
|
559
|
+
parsed = json.loads(body[start:end + 1])
|
|
560
|
+
except Exception:
|
|
561
|
+
return None
|
|
562
|
+
if not isinstance(parsed, dict):
|
|
563
|
+
return None
|
|
564
|
+
weight = parsed.get("weight")
|
|
565
|
+
if weight is None:
|
|
566
|
+
weight = {}
|
|
567
|
+
if not isinstance(weight, dict):
|
|
568
|
+
return None
|
|
569
|
+
typed: Dict[str, Dict[str, float]] = {}
|
|
570
|
+
for name, values in weight.items():
|
|
571
|
+
if not isinstance(values, dict):
|
|
572
|
+
return None
|
|
573
|
+
entry: Dict[str, float] = {}
|
|
574
|
+
for token, w in values.items():
|
|
575
|
+
if isinstance(w, bool) or not isinstance(w, (int, float)):
|
|
576
|
+
return None
|
|
577
|
+
entry[str(token)] = float(w)
|
|
578
|
+
typed[str(name)] = entry
|
|
579
|
+
because = parsed.get("because") or {}
|
|
580
|
+
if not isinstance(because, dict):
|
|
581
|
+
because = {}
|
|
582
|
+
return WeightedProposal(
|
|
583
|
+
weight=typed, because={str(k): str(v) for k, v in because.items()})
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def admit_weighted_restriction(
|
|
587
|
+
proposal: Optional[WeightedProposal],
|
|
588
|
+
*,
|
|
589
|
+
domains: Mapping[str, Sequence[Any]],
|
|
590
|
+
max_weight_ratio: float = 8.0,
|
|
591
|
+
) -> PriorVerdict:
|
|
592
|
+
"""Refuse, or admit with the concentration measured. Never repair.
|
|
593
|
+
|
|
594
|
+
Refusals, in the order a wrong prior does damage:
|
|
595
|
+
|
|
596
|
+
``unparsed`` there is no typed proposal to judge.
|
|
597
|
+
``empty`` it weights nothing (or weights everything equally), so
|
|
598
|
+
there is nothing to measure and nothing to admit -- an
|
|
599
|
+
honest no-op, recorded as one.
|
|
600
|
+
``undeclared`` a parameter or a value the schema never declared. The
|
|
601
|
+
whole reply goes, not the offending entry: a proposal
|
|
602
|
+
that is wrong about the schema has not read the schema,
|
|
603
|
+
and dropping only the bad line would admit the rest on
|
|
604
|
+
the strength of an author that demonstrably guessed.
|
|
605
|
+
``invalid_weight`` a weight that is not a positive finite number. Same
|
|
606
|
+
whole-reply rule.
|
|
607
|
+
``over_concentrated`` some parameter's heaviest value outweighs its
|
|
608
|
+
lightest (implicit ``1.0`` included) by more than
|
|
609
|
+
``max_weight_ratio``. The cap is what keeps a graded
|
|
610
|
+
bias from becoming a de-facto exclusion.
|
|
611
|
+
|
|
612
|
+
What is NOT here, deliberately: ``excludes_front``. The admitted prior is
|
|
613
|
+
a :class:`~agent_evolve.policies.weighted_prior.WeightedRestriction`
|
|
614
|
+
built over the FULL declared domain of every named parameter with
|
|
615
|
+
strictly positive weights, so its support IS the declared domain: it can
|
|
616
|
+
exclude nothing, the predicate the hard form had to gate on is satisfied
|
|
617
|
+
structurally, and the W1 vacuous-or-veto pathology (a large front leaves
|
|
618
|
+
nothing an admissible hard restriction may drop) has no lever to act on.
|
|
619
|
+
"""
|
|
620
|
+
|
|
621
|
+
if proposal is None:
|
|
622
|
+
return PriorVerdict(False, "unparsed")
|
|
623
|
+
weight = {k: dict(v) for k, v in dict(proposal.weight).items() if v}
|
|
624
|
+
if not weight:
|
|
625
|
+
return PriorVerdict(False, "empty", proposal)
|
|
626
|
+
|
|
627
|
+
declared = {str(k): [_token(v) for v in (values or ())]
|
|
628
|
+
for k, values in dict(domains).items()}
|
|
629
|
+
concentration = 1.0
|
|
630
|
+
for name, values in weight.items():
|
|
631
|
+
if name not in declared or not declared[name]:
|
|
632
|
+
return PriorVerdict(
|
|
633
|
+
False, f"undeclared parameter {name!r}", proposal)
|
|
634
|
+
allowed = set(declared[name])
|
|
635
|
+
for token, w in values.items():
|
|
636
|
+
if token not in allowed:
|
|
637
|
+
return PriorVerdict(
|
|
638
|
+
False, f"undeclared value for {name!r}", proposal)
|
|
639
|
+
if not math.isfinite(w) or w <= 0.0:
|
|
640
|
+
return PriorVerdict(
|
|
641
|
+
False, f"invalid_weight for {name!r}", proposal)
|
|
642
|
+
# Implicit 1.0 for every declared value the reply does not name.
|
|
643
|
+
full = [values.get(token, 1.0) for token in allowed]
|
|
644
|
+
ratio = max(full) / min(full)
|
|
645
|
+
concentration = max(concentration, ratio)
|
|
646
|
+
|
|
647
|
+
if concentration <= 1.0:
|
|
648
|
+
return PriorVerdict(False, "empty", proposal, concentration=concentration)
|
|
649
|
+
if concentration > float(max_weight_ratio):
|
|
650
|
+
return PriorVerdict(
|
|
651
|
+
False, f"over_concentrated ({concentration:.3g}x > "
|
|
652
|
+
f"{float(max_weight_ratio):g}x)", proposal,
|
|
653
|
+
concentration=concentration)
|
|
654
|
+
|
|
655
|
+
from agent_evolve.policies.weighted_prior import WeightedRestriction
|
|
656
|
+
|
|
657
|
+
weighted: Dict[str, Tuple[Tuple[Any, ...], Tuple[float, ...]]] = {}
|
|
658
|
+
for name, values in weight.items():
|
|
659
|
+
declared_values = tuple(dict(domains)[name])
|
|
660
|
+
weighted[name] = (declared_values,
|
|
661
|
+
tuple(float(values.get(_token(v), 1.0))
|
|
662
|
+
for v in declared_values))
|
|
663
|
+
return PriorVerdict(True, "admitted", proposal,
|
|
664
|
+
prior=WeightedRestriction(weighted),
|
|
665
|
+
concentration=concentration)
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def apply_weighted_restriction(
|
|
669
|
+
domains: Mapping[str, Sequence[Any]],
|
|
670
|
+
prior: "WeightedRestriction",
|
|
671
|
+
) -> Dict[str, List[Any]]:
|
|
672
|
+
"""*domains* with sampling mass biased by an ADMITTED *prior*.
|
|
673
|
+
|
|
674
|
+
The generic ``weights_for`` seam biases draws the HARNESS makes; an
|
|
675
|
+
authored sampler draws from whatever list it is handed, so for that path
|
|
676
|
+
the bias must live in the list itself. It is mechanical and
|
|
677
|
+
sampler-agnostic: each declared value appears a number of times
|
|
678
|
+
proportional to its weight (implicit ``1.0`` where unnamed), scaled so
|
|
679
|
+
the lightest value appears exactly once. A generator that draws uniformly
|
|
680
|
+
from the list it is given -- which is all the authored-sampler contract
|
|
681
|
+
promises -- therefore lands its mass where the weights say, and EVERY
|
|
682
|
+
declared value remains present, so nothing is excluded and validation
|
|
683
|
+
against the declared domains never sees an illegal value. The admission
|
|
684
|
+
cap on the weight ratio is also the cap on how long these lists can grow.
|
|
685
|
+
"""
|
|
686
|
+
|
|
687
|
+
entries = dict(getattr(prior, "weighted", {}) or {})
|
|
688
|
+
out: Dict[str, List[Any]] = {}
|
|
689
|
+
for name, values in dict(domains).items():
|
|
690
|
+
entry = entries.get(name)
|
|
691
|
+
if not entry:
|
|
692
|
+
out[name] = list(values)
|
|
693
|
+
continue
|
|
694
|
+
table = {_token(v): float(w) for v, w in zip(*entry)}
|
|
695
|
+
per_value = [(v, table.get(_token(v), 1.0)) for v in values]
|
|
696
|
+
lightest = min((w for _v, w in per_value), default=1.0)
|
|
697
|
+
if lightest <= 0.0: # admission forbids this;
|
|
698
|
+
out[name] = list(values) # degrade, never divide by 0
|
|
699
|
+
continue
|
|
700
|
+
biased: List[Any] = []
|
|
701
|
+
for v, w in per_value:
|
|
702
|
+
biased.extend([v] * max(1, round(w / lightest)))
|
|
703
|
+
out[name] = biased
|
|
704
|
+
return out
|