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,707 @@
|
|
|
1
|
+
"""Precommitted balanced complete-subset blocks for causal memory trials.
|
|
2
|
+
|
|
3
|
+
The planner in this module is deliberately provider- and workload-neutral. It
|
|
4
|
+
binds an immutable :class:`MemoryScoreSnapshot` to an ordered collection of
|
|
5
|
+
stable experimental units *before* any provider request is made. Every full
|
|
6
|
+
block visits each member of the canonical ``C(n, k)`` insight-subset catalog
|
|
7
|
+
exactly once. A trailing partial block samples without replacement from that
|
|
8
|
+
same catalog.
|
|
9
|
+
|
|
10
|
+
Randomness is kept outside the policy. Callers supply exact integer ranks:
|
|
11
|
+
|
|
12
|
+
* one permutation rank in ``[0, C(n,k)!)`` for every complete block;
|
|
13
|
+
* for a remainder of size ``r``, one combination rank in
|
|
14
|
+
``[0, C(C(n,k),r))`` and one permutation rank in ``[0, r!)``.
|
|
15
|
+
|
|
16
|
+
The planner only maps those ranks to a frozen schedule. Consequently the
|
|
17
|
+
randomization law is exact, easy to replay, and independent of provider
|
|
18
|
+
latency, completion order, evaluator output, or benchmark semantics.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import hashlib
|
|
24
|
+
import itertools
|
|
25
|
+
import json
|
|
26
|
+
import math
|
|
27
|
+
import re
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from typing import overload
|
|
30
|
+
|
|
31
|
+
from agent_evolve.domain.insight import InsightRef
|
|
32
|
+
from agent_evolve.policies.memory.randomized_subset import (
|
|
33
|
+
InsightSelectionDecision,
|
|
34
|
+
InsightSelectionMode,
|
|
35
|
+
)
|
|
36
|
+
from agent_evolve.policies.memory.staged_causal import (
|
|
37
|
+
DeterministicMemoryControlPolicy,
|
|
38
|
+
MemoryScoreSnapshot,
|
|
39
|
+
insight_selection_decision_sha256,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
BALANCED_SUBSET_BLOCK_POLICY_ID = "balanced_complete_k_subset_blocks"
|
|
44
|
+
BALANCED_SUBSET_BLOCK_POLICY_VERSION = 1
|
|
45
|
+
_POLICY_DEFINITION = (
|
|
46
|
+
b"agent-evolve:balanced-complete-k-subset-blocks:v1:"
|
|
47
|
+
b"canonical-lexicographic-catalog;externally-ranked-full-permutations;"
|
|
48
|
+
b"externally-ranked-no-replacement-remainder;immutable-snapshot-and-units;"
|
|
49
|
+
b"uniform-control-materialization;treated-and-control-support-preflight"
|
|
50
|
+
)
|
|
51
|
+
BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
52
|
+
_POLICY_DEFINITION
|
|
53
|
+
).hexdigest()
|
|
54
|
+
_PLAN_RECEIPT_DOMAIN = b"agent-evolve:balanced-subset-block-plan:v1\x00"
|
|
55
|
+
_SAFE_UNIT_TOKEN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _canonical_json(value: object) -> bytes:
|
|
59
|
+
return json.dumps(
|
|
60
|
+
value,
|
|
61
|
+
allow_nan=False,
|
|
62
|
+
ensure_ascii=True,
|
|
63
|
+
separators=(",", ":"),
|
|
64
|
+
sort_keys=True,
|
|
65
|
+
).encode("ascii", errors="strict")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _hash_record(domain: bytes, value: object) -> str:
|
|
69
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _reference_record(reference: InsightRef) -> dict[str, object]:
|
|
73
|
+
return {
|
|
74
|
+
"insight_id": reference.insight_id.value,
|
|
75
|
+
"version": reference.version,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _require_unit_token(value: str, name: str) -> None:
|
|
80
|
+
if type(value) is not str or _SAFE_UNIT_TOKEN.fullmatch(value) is None:
|
|
81
|
+
raise ValueError(f"{name} must be a bounded durable identifier")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _require_rank(rank: int, *, stop: int, name: str) -> None:
|
|
85
|
+
if type(rank) is not int or rank < 0 or rank >= stop:
|
|
86
|
+
raise ValueError(f"{name} must lie in [0, {stop})")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _unrank_combination(
|
|
90
|
+
values: tuple[int, ...],
|
|
91
|
+
subset_size: int,
|
|
92
|
+
rank: int,
|
|
93
|
+
*,
|
|
94
|
+
name: str,
|
|
95
|
+
) -> tuple[int, ...]:
|
|
96
|
+
"""Map one exact lexicographic combination rank to its members."""
|
|
97
|
+
|
|
98
|
+
combination_count = math.comb(len(values), subset_size)
|
|
99
|
+
_require_rank(rank, stop=combination_count, name=name)
|
|
100
|
+
selected_indices: list[int] = []
|
|
101
|
+
remaining_rank = rank
|
|
102
|
+
start = 0
|
|
103
|
+
for position in range(subset_size):
|
|
104
|
+
remaining_positions = subset_size - position - 1
|
|
105
|
+
for index in range(start, len(values)):
|
|
106
|
+
suffix_count = math.comb(
|
|
107
|
+
len(values) - index - 1,
|
|
108
|
+
remaining_positions,
|
|
109
|
+
)
|
|
110
|
+
if remaining_rank < suffix_count:
|
|
111
|
+
selected_indices.append(index)
|
|
112
|
+
start = index + 1
|
|
113
|
+
break
|
|
114
|
+
remaining_rank -= suffix_count
|
|
115
|
+
return tuple(values[index] for index in selected_indices)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _unrank_permutation(
|
|
119
|
+
values: tuple[int, ...],
|
|
120
|
+
rank: int,
|
|
121
|
+
*,
|
|
122
|
+
name: str,
|
|
123
|
+
) -> tuple[int, ...]:
|
|
124
|
+
"""Map one exact Lehmer rank to a permutation of ``values``."""
|
|
125
|
+
|
|
126
|
+
permutation_count = math.factorial(len(values))
|
|
127
|
+
_require_rank(rank, stop=permutation_count, name=name)
|
|
128
|
+
remaining = list(values)
|
|
129
|
+
result: list[int] = []
|
|
130
|
+
remaining_rank = rank
|
|
131
|
+
for slots in range(len(values), 0, -1):
|
|
132
|
+
block_size = math.factorial(slots - 1)
|
|
133
|
+
index, remaining_rank = divmod(remaining_rank, block_size)
|
|
134
|
+
result.append(remaining.pop(index))
|
|
135
|
+
return tuple(result)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def canonical_memory_subset_catalog(
|
|
139
|
+
snapshot: MemoryScoreSnapshot,
|
|
140
|
+
subset_size: int,
|
|
141
|
+
) -> tuple[tuple[InsightRef, ...], ...]:
|
|
142
|
+
"""Return the canonical lexicographic ``C(n,k)`` catalog for a snapshot."""
|
|
143
|
+
|
|
144
|
+
if not isinstance(snapshot, MemoryScoreSnapshot):
|
|
145
|
+
raise TypeError("snapshot must be a MemoryScoreSnapshot")
|
|
146
|
+
references = tuple(entry.reference for entry in snapshot.entries)
|
|
147
|
+
if type(subset_size) is not int:
|
|
148
|
+
raise TypeError("subset_size must be an exact integer")
|
|
149
|
+
if subset_size <= 0 or subset_size >= len(references):
|
|
150
|
+
raise ValueError(
|
|
151
|
+
"subset_size must lie strictly between zero and the snapshot size "
|
|
152
|
+
"so treated and control support are possible"
|
|
153
|
+
)
|
|
154
|
+
return tuple(itertools.combinations(references, subset_size))
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass(frozen=True, slots=True)
|
|
158
|
+
class StableMemoryAssignmentUnit:
|
|
159
|
+
"""One prospectively named provider-call slot in the frozen schedule."""
|
|
160
|
+
|
|
161
|
+
unit_key: str
|
|
162
|
+
generation: int
|
|
163
|
+
lane_id: str
|
|
164
|
+
|
|
165
|
+
def __post_init__(self) -> None:
|
|
166
|
+
_require_unit_token(self.unit_key, "unit_key")
|
|
167
|
+
if type(self.generation) is not int or self.generation < 0:
|
|
168
|
+
raise ValueError("generation must be a non-negative exact integer")
|
|
169
|
+
_require_unit_token(self.lane_id, "lane_id")
|
|
170
|
+
|
|
171
|
+
def to_record(self) -> dict[str, object]:
|
|
172
|
+
self.__post_init__()
|
|
173
|
+
return {
|
|
174
|
+
"unit_key": self.unit_key,
|
|
175
|
+
"generation": self.generation,
|
|
176
|
+
"lane_id": self.lane_id,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@dataclass(frozen=True, slots=True)
|
|
181
|
+
class MemorySubsetSupport:
|
|
182
|
+
"""Realized selected/unselected support for one exact insight version."""
|
|
183
|
+
|
|
184
|
+
reference: InsightRef
|
|
185
|
+
treated_count: int
|
|
186
|
+
control_count: int
|
|
187
|
+
|
|
188
|
+
def __post_init__(self) -> None:
|
|
189
|
+
if not isinstance(self.reference, InsightRef):
|
|
190
|
+
raise TypeError("reference must be an InsightRef")
|
|
191
|
+
for name in ("treated_count", "control_count"):
|
|
192
|
+
value = getattr(self, name)
|
|
193
|
+
if type(value) is not int or value < 0:
|
|
194
|
+
raise ValueError(f"{name} must be a non-negative exact integer")
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def has_overlap(self) -> bool:
|
|
198
|
+
"""Whether both treated and control observations are scheduled."""
|
|
199
|
+
|
|
200
|
+
return self.treated_count > 0 and self.control_count > 0
|
|
201
|
+
|
|
202
|
+
def to_record(self) -> dict[str, object]:
|
|
203
|
+
self.__post_init__()
|
|
204
|
+
return {
|
|
205
|
+
"reference": _reference_record(self.reference),
|
|
206
|
+
"treated_count": self.treated_count,
|
|
207
|
+
"control_count": self.control_count,
|
|
208
|
+
"has_overlap": self.has_overlap,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@dataclass(frozen=True, slots=True)
|
|
213
|
+
class BalancedSubsetBlockAssignment:
|
|
214
|
+
"""One stable unit's exact catalog assignment and uniform decision."""
|
|
215
|
+
|
|
216
|
+
schedule_position: int
|
|
217
|
+
unit: StableMemoryAssignmentUnit
|
|
218
|
+
block_index: int
|
|
219
|
+
block_position: int
|
|
220
|
+
is_remainder: bool
|
|
221
|
+
subset_rank: int
|
|
222
|
+
decision: InsightSelectionDecision
|
|
223
|
+
|
|
224
|
+
def __post_init__(self) -> None:
|
|
225
|
+
for name in (
|
|
226
|
+
"schedule_position",
|
|
227
|
+
"block_index",
|
|
228
|
+
"block_position",
|
|
229
|
+
"subset_rank",
|
|
230
|
+
):
|
|
231
|
+
value = getattr(self, name)
|
|
232
|
+
if type(value) is not int or value < 0:
|
|
233
|
+
raise ValueError(f"{name} must be a non-negative exact integer")
|
|
234
|
+
if type(self.unit) is not StableMemoryAssignmentUnit:
|
|
235
|
+
raise TypeError("unit must be an exact StableMemoryAssignmentUnit")
|
|
236
|
+
self.unit.__post_init__()
|
|
237
|
+
if type(self.is_remainder) is not bool:
|
|
238
|
+
raise TypeError("is_remainder must be a bool")
|
|
239
|
+
if not isinstance(self.decision, InsightSelectionDecision):
|
|
240
|
+
raise TypeError("decision must be an InsightSelectionDecision")
|
|
241
|
+
if self.decision.mode is not InsightSelectionMode.EXPLORE_UNIFORM:
|
|
242
|
+
raise ValueError("balanced block assignments must use uniform controls")
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def selection_decision_sha256(self) -> str:
|
|
246
|
+
return insight_selection_decision_sha256(self.decision)
|
|
247
|
+
|
|
248
|
+
def to_record(self) -> dict[str, object]:
|
|
249
|
+
self.__post_init__()
|
|
250
|
+
probability = self.decision.selected_subset_probability
|
|
251
|
+
return {
|
|
252
|
+
"schedule_position": self.schedule_position,
|
|
253
|
+
"unit": self.unit.to_record(),
|
|
254
|
+
"block_index": self.block_index,
|
|
255
|
+
"block_position": self.block_position,
|
|
256
|
+
"is_remainder": self.is_remainder,
|
|
257
|
+
"subset_rank": self.subset_rank,
|
|
258
|
+
"selected": [
|
|
259
|
+
_reference_record(reference) for reference in self.decision.selected
|
|
260
|
+
],
|
|
261
|
+
"selected_subset_probability": [
|
|
262
|
+
probability.numerator,
|
|
263
|
+
probability.denominator,
|
|
264
|
+
],
|
|
265
|
+
"selection_decision_sha256": self.selection_decision_sha256,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _validate_ordered_units(
|
|
270
|
+
ordered_units: tuple[StableMemoryAssignmentUnit, ...],
|
|
271
|
+
) -> None:
|
|
272
|
+
if type(ordered_units) is not tuple or not ordered_units:
|
|
273
|
+
raise ValueError("ordered_units must be a non-empty exact tuple")
|
|
274
|
+
if any(type(unit) is not StableMemoryAssignmentUnit for unit in ordered_units):
|
|
275
|
+
raise TypeError(
|
|
276
|
+
"ordered_units must contain exact StableMemoryAssignmentUnit values"
|
|
277
|
+
)
|
|
278
|
+
for unit in ordered_units:
|
|
279
|
+
unit.__post_init__()
|
|
280
|
+
unit_keys = tuple(unit.unit_key for unit in ordered_units)
|
|
281
|
+
if len(set(unit_keys)) != len(unit_keys):
|
|
282
|
+
raise ValueError("ordered_units cannot repeat a stable unit_key")
|
|
283
|
+
generation_lanes = tuple((unit.generation, unit.lane_id) for unit in ordered_units)
|
|
284
|
+
if len(set(generation_lanes)) != len(generation_lanes):
|
|
285
|
+
raise ValueError("ordered_units cannot repeat a generation/lane pair")
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _scheduled_subset_ranks(
|
|
289
|
+
*,
|
|
290
|
+
unit_count: int,
|
|
291
|
+
catalog_size: int,
|
|
292
|
+
full_block_permutation_ranks: tuple[int, ...],
|
|
293
|
+
remainder_selection_rank: int | None,
|
|
294
|
+
remainder_permutation_rank: int | None,
|
|
295
|
+
) -> tuple[int, ...]:
|
|
296
|
+
full_block_count, remainder_size = divmod(unit_count, catalog_size)
|
|
297
|
+
if type(full_block_permutation_ranks) is not tuple:
|
|
298
|
+
raise TypeError("full_block_permutation_ranks must be an exact tuple")
|
|
299
|
+
if len(full_block_permutation_ranks) != full_block_count:
|
|
300
|
+
raise ValueError(
|
|
301
|
+
"full_block_permutation_ranks must provide exactly one rank per "
|
|
302
|
+
"complete catalog block"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
catalog_ranks = tuple(range(catalog_size))
|
|
306
|
+
schedule: list[int] = []
|
|
307
|
+
for block_index, permutation_rank in enumerate(full_block_permutation_ranks):
|
|
308
|
+
schedule.extend(
|
|
309
|
+
_unrank_permutation(
|
|
310
|
+
catalog_ranks,
|
|
311
|
+
permutation_rank,
|
|
312
|
+
name=f"full_block_permutation_ranks[{block_index}]",
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if remainder_size == 0:
|
|
317
|
+
if remainder_selection_rank is not None:
|
|
318
|
+
raise ValueError(
|
|
319
|
+
"remainder_selection_rank must be None when there is no remainder"
|
|
320
|
+
)
|
|
321
|
+
if remainder_permutation_rank is not None:
|
|
322
|
+
raise ValueError(
|
|
323
|
+
"remainder_permutation_rank must be None when there is no remainder"
|
|
324
|
+
)
|
|
325
|
+
return tuple(schedule)
|
|
326
|
+
|
|
327
|
+
if remainder_selection_rank is None or remainder_permutation_rank is None:
|
|
328
|
+
raise ValueError(
|
|
329
|
+
"a partial block requires exact remainder selection and permutation ranks"
|
|
330
|
+
)
|
|
331
|
+
selected = _unrank_combination(
|
|
332
|
+
catalog_ranks,
|
|
333
|
+
remainder_size,
|
|
334
|
+
remainder_selection_rank,
|
|
335
|
+
name="remainder_selection_rank",
|
|
336
|
+
)
|
|
337
|
+
schedule.extend(
|
|
338
|
+
_unrank_permutation(
|
|
339
|
+
selected,
|
|
340
|
+
remainder_permutation_rank,
|
|
341
|
+
name="remainder_permutation_rank",
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
return tuple(schedule)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _support_for(
|
|
348
|
+
references: tuple[InsightRef, ...],
|
|
349
|
+
assignments: tuple[BalancedSubsetBlockAssignment, ...],
|
|
350
|
+
) -> tuple[MemorySubsetSupport, ...]:
|
|
351
|
+
return tuple(
|
|
352
|
+
MemorySubsetSupport(
|
|
353
|
+
reference=reference,
|
|
354
|
+
treated_count=sum(
|
|
355
|
+
reference in assignment.decision.selected for assignment in assignments
|
|
356
|
+
),
|
|
357
|
+
control_count=sum(
|
|
358
|
+
reference not in assignment.decision.selected
|
|
359
|
+
for assignment in assignments
|
|
360
|
+
),
|
|
361
|
+
)
|
|
362
|
+
for reference in references
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
367
|
+
class BalancedSubsetBlockPlan:
|
|
368
|
+
"""Authenticated pre-provider schedule and its replay receipt."""
|
|
369
|
+
|
|
370
|
+
snapshot: MemoryScoreSnapshot
|
|
371
|
+
ordered_units: tuple[StableMemoryAssignmentUnit, ...]
|
|
372
|
+
subset_size: int
|
|
373
|
+
catalog: tuple[tuple[InsightRef, ...], ...]
|
|
374
|
+
full_block_permutation_ranks: tuple[int, ...]
|
|
375
|
+
remainder_selection_rank: int | None
|
|
376
|
+
remainder_permutation_rank: int | None
|
|
377
|
+
assignments: tuple[BalancedSubsetBlockAssignment, ...]
|
|
378
|
+
support: tuple[MemorySubsetSupport, ...]
|
|
379
|
+
policy_id: str = BALANCED_SUBSET_BLOCK_POLICY_ID
|
|
380
|
+
policy_version: int = BALANCED_SUBSET_BLOCK_POLICY_VERSION
|
|
381
|
+
policy_definition_sha256: str = BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256
|
|
382
|
+
|
|
383
|
+
def __post_init__(self) -> None:
|
|
384
|
+
if not isinstance(self.snapshot, MemoryScoreSnapshot):
|
|
385
|
+
raise TypeError("snapshot must be a MemoryScoreSnapshot")
|
|
386
|
+
_validate_ordered_units(self.ordered_units)
|
|
387
|
+
expected_catalog = canonical_memory_subset_catalog(
|
|
388
|
+
self.snapshot,
|
|
389
|
+
self.subset_size,
|
|
390
|
+
)
|
|
391
|
+
if self.catalog != expected_catalog:
|
|
392
|
+
raise ValueError("catalog is not the canonical C(n,k) subset catalog")
|
|
393
|
+
if self.policy_id != BALANCED_SUBSET_BLOCK_POLICY_ID:
|
|
394
|
+
raise ValueError("plan names an unsupported policy_id")
|
|
395
|
+
if self.policy_version != BALANCED_SUBSET_BLOCK_POLICY_VERSION:
|
|
396
|
+
raise ValueError("plan names an unsupported policy_version")
|
|
397
|
+
if (
|
|
398
|
+
self.policy_definition_sha256
|
|
399
|
+
!= BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256
|
|
400
|
+
):
|
|
401
|
+
raise ValueError("balanced subset-block policy definition changed")
|
|
402
|
+
|
|
403
|
+
scheduled_ranks = _scheduled_subset_ranks(
|
|
404
|
+
unit_count=len(self.ordered_units),
|
|
405
|
+
catalog_size=len(self.catalog),
|
|
406
|
+
full_block_permutation_ranks=self.full_block_permutation_ranks,
|
|
407
|
+
remainder_selection_rank=self.remainder_selection_rank,
|
|
408
|
+
remainder_permutation_rank=self.remainder_permutation_rank,
|
|
409
|
+
)
|
|
410
|
+
if type(self.assignments) is not tuple or len(self.assignments) != len(
|
|
411
|
+
self.ordered_units
|
|
412
|
+
):
|
|
413
|
+
raise ValueError("assignments must cover every ordered unit exactly once")
|
|
414
|
+
if any(
|
|
415
|
+
type(value) is not BalancedSubsetBlockAssignment
|
|
416
|
+
for value in self.assignments
|
|
417
|
+
):
|
|
418
|
+
raise TypeError(
|
|
419
|
+
"assignments must contain exact BalancedSubsetBlockAssignment values"
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
controls = DeterministicMemoryControlPolicy()
|
|
423
|
+
catalog_size = len(self.catalog)
|
|
424
|
+
full_block_count = len(self.ordered_units) // catalog_size
|
|
425
|
+
for position, (unit, subset_rank, assignment) in enumerate(
|
|
426
|
+
zip(
|
|
427
|
+
self.ordered_units,
|
|
428
|
+
scheduled_ranks,
|
|
429
|
+
self.assignments,
|
|
430
|
+
strict=True,
|
|
431
|
+
)
|
|
432
|
+
):
|
|
433
|
+
assignment.__post_init__()
|
|
434
|
+
expected_decision = controls.uniform(
|
|
435
|
+
snapshot=self.snapshot,
|
|
436
|
+
subset_size=self.subset_size,
|
|
437
|
+
subset_rank=subset_rank,
|
|
438
|
+
)
|
|
439
|
+
expected_block_index, expected_block_position = divmod(
|
|
440
|
+
position,
|
|
441
|
+
catalog_size,
|
|
442
|
+
)
|
|
443
|
+
expected_remainder = expected_block_index >= full_block_count
|
|
444
|
+
if (
|
|
445
|
+
assignment.schedule_position != position
|
|
446
|
+
or assignment.unit != unit
|
|
447
|
+
or assignment.block_index != expected_block_index
|
|
448
|
+
or assignment.block_position != expected_block_position
|
|
449
|
+
or assignment.is_remainder is not expected_remainder
|
|
450
|
+
or assignment.subset_rank != subset_rank
|
|
451
|
+
or assignment.decision != expected_decision
|
|
452
|
+
):
|
|
453
|
+
raise ValueError(
|
|
454
|
+
"assignment does not replay from the frozen snapshot, units, "
|
|
455
|
+
"and exact ranks"
|
|
456
|
+
)
|
|
457
|
+
if assignment.decision.selected != self.catalog[subset_rank]:
|
|
458
|
+
raise ValueError("assignment decision differs from its catalog subset")
|
|
459
|
+
|
|
460
|
+
references = tuple(entry.reference for entry in self.snapshot.entries)
|
|
461
|
+
expected_support = _support_for(references, self.assignments)
|
|
462
|
+
if self.support != expected_support:
|
|
463
|
+
raise ValueError("support report does not match the frozen assignments")
|
|
464
|
+
unsupported = tuple(value for value in self.support if not value.has_overlap)
|
|
465
|
+
if unsupported:
|
|
466
|
+
details = ", ".join(
|
|
467
|
+
f"{value.reference.insight_id.value}@{value.reference.version}"
|
|
468
|
+
f"(treated={value.treated_count},control={value.control_count})"
|
|
469
|
+
for value in unsupported
|
|
470
|
+
)
|
|
471
|
+
raise ValueError(
|
|
472
|
+
"balanced subset-block preflight requires treated and control "
|
|
473
|
+
f"support for every insight; missing overlap: {details}"
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
@property
|
|
477
|
+
def full_block_count(self) -> int:
|
|
478
|
+
return len(self.ordered_units) // len(self.catalog)
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def remainder_size(self) -> int:
|
|
482
|
+
return len(self.ordered_units) % len(self.catalog)
|
|
483
|
+
|
|
484
|
+
@overload
|
|
485
|
+
def assignment_for(
|
|
486
|
+
self,
|
|
487
|
+
generation: int,
|
|
488
|
+
lane_id: str,
|
|
489
|
+
*,
|
|
490
|
+
unit_key: None = None,
|
|
491
|
+
) -> BalancedSubsetBlockAssignment: ...
|
|
492
|
+
|
|
493
|
+
@overload
|
|
494
|
+
def assignment_for(
|
|
495
|
+
self,
|
|
496
|
+
generation: str,
|
|
497
|
+
lane_id: None = None,
|
|
498
|
+
*,
|
|
499
|
+
unit_key: None = None,
|
|
500
|
+
) -> BalancedSubsetBlockAssignment: ...
|
|
501
|
+
|
|
502
|
+
@overload
|
|
503
|
+
def assignment_for(
|
|
504
|
+
self,
|
|
505
|
+
generation: None = None,
|
|
506
|
+
lane_id: None = None,
|
|
507
|
+
*,
|
|
508
|
+
unit_key: str,
|
|
509
|
+
) -> BalancedSubsetBlockAssignment: ...
|
|
510
|
+
|
|
511
|
+
def assignment_for(
|
|
512
|
+
self,
|
|
513
|
+
generation: int | str | None = None,
|
|
514
|
+
lane_id: str | None = None,
|
|
515
|
+
*,
|
|
516
|
+
unit_key: str | None = None,
|
|
517
|
+
) -> BalancedSubsetBlockAssignment:
|
|
518
|
+
"""Resolve by ``(generation, lane_id)`` or by one stable unit key."""
|
|
519
|
+
|
|
520
|
+
self.__post_init__()
|
|
521
|
+
if unit_key is not None:
|
|
522
|
+
if generation is not None or lane_id is not None:
|
|
523
|
+
raise ValueError(
|
|
524
|
+
"unit_key cannot be combined with generation or lane_id"
|
|
525
|
+
)
|
|
526
|
+
_require_unit_token(unit_key, "unit_key")
|
|
527
|
+
matches = tuple(
|
|
528
|
+
value for value in self.assignments if value.unit.unit_key == unit_key
|
|
529
|
+
)
|
|
530
|
+
elif type(generation) is str and lane_id is None:
|
|
531
|
+
_require_unit_token(generation, "unit_key")
|
|
532
|
+
matches = tuple(
|
|
533
|
+
value for value in self.assignments if value.unit.unit_key == generation
|
|
534
|
+
)
|
|
535
|
+
else:
|
|
536
|
+
if type(generation) is not int or generation < 0:
|
|
537
|
+
raise ValueError(
|
|
538
|
+
"generation must be a non-negative exact integer when "
|
|
539
|
+
"resolving by lane"
|
|
540
|
+
)
|
|
541
|
+
if lane_id is None:
|
|
542
|
+
raise ValueError("lane_id is required when resolving by generation")
|
|
543
|
+
_require_unit_token(lane_id, "lane_id")
|
|
544
|
+
matches = tuple(
|
|
545
|
+
value
|
|
546
|
+
for value in self.assignments
|
|
547
|
+
if value.unit.generation == generation and value.unit.lane_id == lane_id
|
|
548
|
+
)
|
|
549
|
+
if not matches:
|
|
550
|
+
raise KeyError("no balanced memory assignment matches the stable unit")
|
|
551
|
+
if len(matches) != 1: # pragma: no cover - plan validation forbids this.
|
|
552
|
+
raise RuntimeError("stable memory assignment identity is ambiguous")
|
|
553
|
+
return matches[0]
|
|
554
|
+
|
|
555
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
556
|
+
return {
|
|
557
|
+
"schema_version": 1,
|
|
558
|
+
"policy": {
|
|
559
|
+
"policy_id": self.policy_id,
|
|
560
|
+
"policy_version": self.policy_version,
|
|
561
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
562
|
+
},
|
|
563
|
+
"score_snapshot_sha256": self.snapshot.snapshot_sha256,
|
|
564
|
+
"subset_size": self.subset_size,
|
|
565
|
+
"catalog": [
|
|
566
|
+
[_reference_record(reference) for reference in subset]
|
|
567
|
+
for subset in self.catalog
|
|
568
|
+
],
|
|
569
|
+
"ordered_units": [unit.to_record() for unit in self.ordered_units],
|
|
570
|
+
"exact_ranks": {
|
|
571
|
+
"full_block_permutation_ranks": list(self.full_block_permutation_ranks),
|
|
572
|
+
"remainder_selection_rank": self.remainder_selection_rank,
|
|
573
|
+
"remainder_permutation_rank": self.remainder_permutation_rank,
|
|
574
|
+
},
|
|
575
|
+
"full_block_count": self.full_block_count,
|
|
576
|
+
"remainder_size": self.remainder_size,
|
|
577
|
+
"assignments": [value.to_record() for value in self.assignments],
|
|
578
|
+
"support": [value.to_record() for value in self.support],
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
@property
|
|
582
|
+
def receipt_sha256(self) -> str:
|
|
583
|
+
"""Digest authenticating the complete prospective plan and receipt."""
|
|
584
|
+
|
|
585
|
+
self.__post_init__()
|
|
586
|
+
return _hash_record(_PLAN_RECEIPT_DOMAIN, self._unsigned_record())
|
|
587
|
+
|
|
588
|
+
@property
|
|
589
|
+
def plan_sha256(self) -> str:
|
|
590
|
+
"""Alias emphasizing that the receipt authenticates the whole plan."""
|
|
591
|
+
|
|
592
|
+
return self.receipt_sha256
|
|
593
|
+
|
|
594
|
+
def to_record(self) -> dict[str, object]:
|
|
595
|
+
self.__post_init__()
|
|
596
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
597
|
+
|
|
598
|
+
def __eq__(self, other: object) -> bool:
|
|
599
|
+
return (
|
|
600
|
+
type(other) is BalancedSubsetBlockPlan
|
|
601
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
__hash__ = None
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
@dataclass(frozen=True, slots=True)
|
|
608
|
+
class BalancedSubsetBlockPlanner:
|
|
609
|
+
"""Map externally generated exact ranks to a frozen balanced schedule."""
|
|
610
|
+
|
|
611
|
+
def plan(
|
|
612
|
+
self,
|
|
613
|
+
*,
|
|
614
|
+
snapshot: MemoryScoreSnapshot,
|
|
615
|
+
ordered_units: tuple[StableMemoryAssignmentUnit, ...],
|
|
616
|
+
subset_size: int,
|
|
617
|
+
full_block_permutation_ranks: tuple[int, ...],
|
|
618
|
+
remainder_selection_rank: int | None = None,
|
|
619
|
+
remainder_permutation_rank: int | None = None,
|
|
620
|
+
) -> BalancedSubsetBlockPlan:
|
|
621
|
+
"""Preflight, materialize, and authenticate one prospective schedule.
|
|
622
|
+
|
|
623
|
+
Complete blocks are contiguous slices of ``ordered_units``. A caller
|
|
624
|
+
that wants one complete block per stable lane should therefore supply
|
|
625
|
+
lane-major units; no lane semantics are baked into this policy.
|
|
626
|
+
"""
|
|
627
|
+
|
|
628
|
+
catalog = canonical_memory_subset_catalog(snapshot, subset_size)
|
|
629
|
+
_validate_ordered_units(ordered_units)
|
|
630
|
+
scheduled_ranks = _scheduled_subset_ranks(
|
|
631
|
+
unit_count=len(ordered_units),
|
|
632
|
+
catalog_size=len(catalog),
|
|
633
|
+
full_block_permutation_ranks=full_block_permutation_ranks,
|
|
634
|
+
remainder_selection_rank=remainder_selection_rank,
|
|
635
|
+
remainder_permutation_rank=remainder_permutation_rank,
|
|
636
|
+
)
|
|
637
|
+
controls = DeterministicMemoryControlPolicy()
|
|
638
|
+
full_block_count = len(ordered_units) // len(catalog)
|
|
639
|
+
assignments = tuple(
|
|
640
|
+
BalancedSubsetBlockAssignment(
|
|
641
|
+
schedule_position=position,
|
|
642
|
+
unit=unit,
|
|
643
|
+
block_index=position // len(catalog),
|
|
644
|
+
block_position=position % len(catalog),
|
|
645
|
+
is_remainder=(position // len(catalog)) >= full_block_count,
|
|
646
|
+
subset_rank=subset_rank,
|
|
647
|
+
decision=controls.uniform(
|
|
648
|
+
snapshot=snapshot,
|
|
649
|
+
subset_size=subset_size,
|
|
650
|
+
subset_rank=subset_rank,
|
|
651
|
+
),
|
|
652
|
+
)
|
|
653
|
+
for position, (unit, subset_rank) in enumerate(
|
|
654
|
+
zip(ordered_units, scheduled_ranks, strict=True)
|
|
655
|
+
)
|
|
656
|
+
)
|
|
657
|
+
references = tuple(entry.reference for entry in snapshot.entries)
|
|
658
|
+
return BalancedSubsetBlockPlan(
|
|
659
|
+
snapshot=snapshot,
|
|
660
|
+
ordered_units=ordered_units,
|
|
661
|
+
subset_size=subset_size,
|
|
662
|
+
catalog=catalog,
|
|
663
|
+
full_block_permutation_ranks=full_block_permutation_ranks,
|
|
664
|
+
remainder_selection_rank=remainder_selection_rank,
|
|
665
|
+
remainder_permutation_rank=remainder_permutation_rank,
|
|
666
|
+
assignments=assignments,
|
|
667
|
+
support=_support_for(references, assignments),
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
def replay(
|
|
671
|
+
self,
|
|
672
|
+
receipt: BalancedSubsetBlockPlan,
|
|
673
|
+
*,
|
|
674
|
+
snapshot: MemoryScoreSnapshot,
|
|
675
|
+
ordered_units: tuple[StableMemoryAssignmentUnit, ...],
|
|
676
|
+
) -> BalancedSubsetBlockPlan:
|
|
677
|
+
"""Rebuild a receipt from its ranks and reject any basis mismatch."""
|
|
678
|
+
|
|
679
|
+
if type(receipt) is not BalancedSubsetBlockPlan:
|
|
680
|
+
raise TypeError("receipt must be an exact BalancedSubsetBlockPlan")
|
|
681
|
+
receipt.__post_init__()
|
|
682
|
+
replayed = self.plan(
|
|
683
|
+
snapshot=snapshot,
|
|
684
|
+
ordered_units=ordered_units,
|
|
685
|
+
subset_size=receipt.subset_size,
|
|
686
|
+
full_block_permutation_ranks=(receipt.full_block_permutation_ranks),
|
|
687
|
+
remainder_selection_rank=receipt.remainder_selection_rank,
|
|
688
|
+
remainder_permutation_rank=receipt.remainder_permutation_rank,
|
|
689
|
+
)
|
|
690
|
+
if replayed.receipt_sha256 != receipt.receipt_sha256:
|
|
691
|
+
raise ValueError(
|
|
692
|
+
"replayed balanced subset-block plan does not match the receipt"
|
|
693
|
+
)
|
|
694
|
+
return replayed
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
__all__ = [
|
|
698
|
+
"BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256",
|
|
699
|
+
"BALANCED_SUBSET_BLOCK_POLICY_ID",
|
|
700
|
+
"BALANCED_SUBSET_BLOCK_POLICY_VERSION",
|
|
701
|
+
"BalancedSubsetBlockAssignment",
|
|
702
|
+
"BalancedSubsetBlockPlan",
|
|
703
|
+
"BalancedSubsetBlockPlanner",
|
|
704
|
+
"MemorySubsetSupport",
|
|
705
|
+
"StableMemoryAssignmentUnit",
|
|
706
|
+
"canonical_memory_subset_catalog",
|
|
707
|
+
]
|