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,593 @@
|
|
|
1
|
+
"""Canonical workload-neutral matching of stable lanes to compatible cards.
|
|
2
|
+
|
|
3
|
+
This policy consumes only immutable identities and a prospectively derived
|
|
4
|
+
bipartite compatibility graph. It knows nothing about prompts, providers,
|
|
5
|
+
finite-action compilers, memory banks, or benchmark configurations. A caller
|
|
6
|
+
must derive every compatibility edge before any current-wave outcome exists.
|
|
7
|
+
|
|
8
|
+
The objective is closed and deterministic:
|
|
9
|
+
|
|
10
|
+
1. maximize the number of assigned lanes;
|
|
11
|
+
2. among maximum matchings, minimize the lane-ordered assignment vector, where
|
|
12
|
+
card keys use lexical order and an unmatched marker sorts after every card.
|
|
13
|
+
|
|
14
|
+
Consequently a feasible graph yields one canonical full (all-lanes) matching.
|
|
15
|
+
An infeasible graph yields the canonical maximum partial matching and a typed,
|
|
16
|
+
hash-authenticated receipt rather than an exception or an implicit fallback.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import hashlib
|
|
22
|
+
import json
|
|
23
|
+
import re
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from enum import Enum
|
|
26
|
+
|
|
27
|
+
from agent_evolve.domain.patch import require_sha256
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
CANONICAL_LANE_CARD_MATCHING_POLICY_ID = (
|
|
31
|
+
"canonical_maximum_lane_card_compatibility_matching"
|
|
32
|
+
)
|
|
33
|
+
CANONICAL_LANE_CARD_MATCHING_POLICY_VERSION = 1
|
|
34
|
+
CANONICAL_LANE_CARD_MATCHING_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
35
|
+
b"agent-evolve:canonical-maximum-lane-card-compatibility-matching:v1;"
|
|
36
|
+
b"canonical-identity-bound-bipartite-graph;maximize-assigned-lanes;"
|
|
37
|
+
b"lexicographically-minimize-lane-ordered-card-vector;unmatched-sorts-last;"
|
|
38
|
+
b"full-means-every-lane-matched;typed-maximum-partial-infeasibility;"
|
|
39
|
+
b"provider-evaluator-and-workload-neutral;exact-replay-receipt"
|
|
40
|
+
).hexdigest()
|
|
41
|
+
|
|
42
|
+
_IDENTIFIER = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
43
|
+
_GRAPH_DOMAIN = b"agent-evolve:lane-card-compatibility-graph:v1\x00"
|
|
44
|
+
_INPUT_DOMAIN = b"agent-evolve:lane-card-matching-input:v1\x00"
|
|
45
|
+
_EDGE_DOMAIN = b"agent-evolve:lane-card-compatibility-edge:v1\x00"
|
|
46
|
+
_RECEIPT_DOMAIN = b"agent-evolve:lane-card-matching-receipt:v1\x00"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _canonical_json(value: object) -> bytes:
|
|
50
|
+
return json.dumps(
|
|
51
|
+
value,
|
|
52
|
+
allow_nan=False,
|
|
53
|
+
ensure_ascii=True,
|
|
54
|
+
separators=(",", ":"),
|
|
55
|
+
sort_keys=True,
|
|
56
|
+
).encode("ascii", errors="strict")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
60
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _require_identifier(value: str, *, name: str) -> None:
|
|
64
|
+
if type(value) is not str or _IDENTIFIER.fullmatch(value) is None:
|
|
65
|
+
raise ValueError(f"{name} must use the canonical identifier grammar")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True, slots=True)
|
|
69
|
+
class LaneCardMatchingLane:
|
|
70
|
+
"""One stable consumer lane and its exact caller-owned identity."""
|
|
71
|
+
|
|
72
|
+
lane_id: str
|
|
73
|
+
lane_identity_sha256: str
|
|
74
|
+
|
|
75
|
+
def __post_init__(self) -> None:
|
|
76
|
+
_require_identifier(self.lane_id, name="lane_id")
|
|
77
|
+
require_sha256(self.lane_identity_sha256, "lane_identity_sha256")
|
|
78
|
+
|
|
79
|
+
def to_record(self) -> dict[str, str]:
|
|
80
|
+
self.__post_init__()
|
|
81
|
+
return {
|
|
82
|
+
"lane_id": self.lane_id,
|
|
83
|
+
"lane_identity_sha256": self.lane_identity_sha256,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True, slots=True)
|
|
88
|
+
class LaneCardMatchingCard:
|
|
89
|
+
"""One assignable memory card and its exact caller-owned identity."""
|
|
90
|
+
|
|
91
|
+
card_key: str
|
|
92
|
+
card_identity_sha256: str
|
|
93
|
+
|
|
94
|
+
def __post_init__(self) -> None:
|
|
95
|
+
_require_identifier(self.card_key, name="card_key")
|
|
96
|
+
require_sha256(self.card_identity_sha256, "card_identity_sha256")
|
|
97
|
+
|
|
98
|
+
def to_record(self) -> dict[str, str]:
|
|
99
|
+
self.__post_init__()
|
|
100
|
+
return {
|
|
101
|
+
"card_key": self.card_key,
|
|
102
|
+
"card_identity_sha256": self.card_identity_sha256,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True, slots=True)
|
|
107
|
+
class LaneCardCompatibility:
|
|
108
|
+
"""One prospectively established lane/card compatibility edge.
|
|
109
|
+
|
|
110
|
+
``compatibility_evidence_sha256`` is supplied by trusted application code.
|
|
111
|
+
For finite-action workloads it should identify the exact support-derivation
|
|
112
|
+
receipt for this lane's current finite contract and this card's semantics.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
lane_id: str
|
|
116
|
+
card_key: str
|
|
117
|
+
compatibility_evidence_sha256: str
|
|
118
|
+
edge_sha256: str = field(init=False)
|
|
119
|
+
|
|
120
|
+
def __post_init__(self) -> None:
|
|
121
|
+
_require_identifier(self.lane_id, name="lane_id")
|
|
122
|
+
_require_identifier(self.card_key, name="card_key")
|
|
123
|
+
require_sha256(
|
|
124
|
+
self.compatibility_evidence_sha256,
|
|
125
|
+
"compatibility_evidence_sha256",
|
|
126
|
+
)
|
|
127
|
+
object.__setattr__(
|
|
128
|
+
self,
|
|
129
|
+
"edge_sha256",
|
|
130
|
+
_hash(_EDGE_DOMAIN, self._unsigned_record()),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def _unsigned_record(self) -> dict[str, str]:
|
|
134
|
+
return {
|
|
135
|
+
"lane_id": self.lane_id,
|
|
136
|
+
"card_key": self.card_key,
|
|
137
|
+
"compatibility_evidence_sha256": self.compatibility_evidence_sha256,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
def to_record(self) -> dict[str, str]:
|
|
141
|
+
self.__post_init__()
|
|
142
|
+
return {**self._unsigned_record(), "edge_sha256": self.edge_sha256}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
146
|
+
class LaneCardMatchingInput:
|
|
147
|
+
"""Canonical exact populations and complete positive compatibility graph."""
|
|
148
|
+
|
|
149
|
+
lanes: tuple[LaneCardMatchingLane, ...]
|
|
150
|
+
cards: tuple[LaneCardMatchingCard, ...]
|
|
151
|
+
compatibilities: tuple[LaneCardCompatibility, ...]
|
|
152
|
+
|
|
153
|
+
def __post_init__(self) -> None:
|
|
154
|
+
if type(self.lanes) is not tuple or not self.lanes or any(
|
|
155
|
+
type(value) is not LaneCardMatchingLane for value in self.lanes
|
|
156
|
+
):
|
|
157
|
+
raise ValueError("lanes must be a non-empty exact lane tuple")
|
|
158
|
+
if type(self.cards) is not tuple or not self.cards or any(
|
|
159
|
+
type(value) is not LaneCardMatchingCard for value in self.cards
|
|
160
|
+
):
|
|
161
|
+
raise ValueError("cards must be a non-empty exact card tuple")
|
|
162
|
+
if type(self.compatibilities) is not tuple or any(
|
|
163
|
+
type(value) is not LaneCardCompatibility
|
|
164
|
+
for value in self.compatibilities
|
|
165
|
+
):
|
|
166
|
+
raise TypeError("compatibilities must be an exact edge tuple")
|
|
167
|
+
for lane in self.lanes:
|
|
168
|
+
lane.__post_init__()
|
|
169
|
+
for card in self.cards:
|
|
170
|
+
card.__post_init__()
|
|
171
|
+
for compatibility in self.compatibilities:
|
|
172
|
+
compatibility.__post_init__()
|
|
173
|
+
|
|
174
|
+
lane_ids = tuple(value.lane_id for value in self.lanes)
|
|
175
|
+
card_keys = tuple(value.card_key for value in self.cards)
|
|
176
|
+
if lane_ids != tuple(sorted(set(lane_ids))):
|
|
177
|
+
raise ValueError("lanes must use unique canonical lane_id order")
|
|
178
|
+
if card_keys != tuple(sorted(set(card_keys))):
|
|
179
|
+
raise ValueError("cards must use unique canonical card_key order")
|
|
180
|
+
edge_keys = tuple(
|
|
181
|
+
(value.lane_id, value.card_key) for value in self.compatibilities
|
|
182
|
+
)
|
|
183
|
+
if edge_keys != tuple(sorted(set(edge_keys))):
|
|
184
|
+
raise ValueError(
|
|
185
|
+
"compatibilities must use unique canonical lane/card order"
|
|
186
|
+
)
|
|
187
|
+
unknown_lanes = tuple(
|
|
188
|
+
lane_id for lane_id, _ in edge_keys if lane_id not in set(lane_ids)
|
|
189
|
+
)
|
|
190
|
+
unknown_cards = tuple(
|
|
191
|
+
card_key for _, card_key in edge_keys if card_key not in set(card_keys)
|
|
192
|
+
)
|
|
193
|
+
if unknown_lanes or unknown_cards:
|
|
194
|
+
raise ValueError("compatibility edge names a foreign lane or card")
|
|
195
|
+
|
|
196
|
+
def _graph_record(self) -> dict[str, object]:
|
|
197
|
+
self.__post_init__()
|
|
198
|
+
return {
|
|
199
|
+
"schema_version": 1,
|
|
200
|
+
"lanes": [value.to_record() for value in self.lanes],
|
|
201
|
+
"cards": [value.to_record() for value in self.cards],
|
|
202
|
+
"compatibilities": [
|
|
203
|
+
value.to_record() for value in self.compatibilities
|
|
204
|
+
],
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def compatibility_graph_sha256(self) -> str:
|
|
209
|
+
return _hash(_GRAPH_DOMAIN, self._graph_record())
|
|
210
|
+
|
|
211
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
212
|
+
return {
|
|
213
|
+
**self._graph_record(),
|
|
214
|
+
"compatibility_graph_sha256": self.compatibility_graph_sha256,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def input_sha256(self) -> str:
|
|
219
|
+
return _hash(_INPUT_DOMAIN, self._unsigned_record())
|
|
220
|
+
|
|
221
|
+
def to_record(self) -> dict[str, object]:
|
|
222
|
+
return {**self._unsigned_record(), "input_sha256": self.input_sha256}
|
|
223
|
+
|
|
224
|
+
def __eq__(self, other: object) -> bool:
|
|
225
|
+
return (
|
|
226
|
+
type(other) is LaneCardMatchingInput
|
|
227
|
+
and self.input_sha256 == other.input_sha256
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
__hash__ = None
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass(frozen=True, slots=True)
|
|
234
|
+
class LaneCardAssignment:
|
|
235
|
+
"""One exact selected edge in the canonical maximum matching."""
|
|
236
|
+
|
|
237
|
+
lane: LaneCardMatchingLane
|
|
238
|
+
card: LaneCardMatchingCard
|
|
239
|
+
compatibility_edge_sha256: str
|
|
240
|
+
|
|
241
|
+
def __post_init__(self) -> None:
|
|
242
|
+
if type(self.lane) is not LaneCardMatchingLane:
|
|
243
|
+
raise TypeError("lane must be an exact LaneCardMatchingLane")
|
|
244
|
+
if type(self.card) is not LaneCardMatchingCard:
|
|
245
|
+
raise TypeError("card must be an exact LaneCardMatchingCard")
|
|
246
|
+
self.lane.__post_init__()
|
|
247
|
+
self.card.__post_init__()
|
|
248
|
+
require_sha256(
|
|
249
|
+
self.compatibility_edge_sha256,
|
|
250
|
+
"compatibility_edge_sha256",
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def to_record(self) -> dict[str, object]:
|
|
254
|
+
self.__post_init__()
|
|
255
|
+
return {
|
|
256
|
+
"lane": self.lane.to_record(),
|
|
257
|
+
"card": self.card.to_record(),
|
|
258
|
+
"compatibility_edge_sha256": self.compatibility_edge_sha256,
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class LaneCardMatchingStatus(str, Enum):
|
|
263
|
+
"""Closed result of the all-lanes matching requirement."""
|
|
264
|
+
|
|
265
|
+
FULL = "full"
|
|
266
|
+
INFEASIBLE = "infeasible"
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _adjacency(
|
|
270
|
+
matching_input: LaneCardMatchingInput,
|
|
271
|
+
) -> dict[str, tuple[str, ...]]:
|
|
272
|
+
values: dict[str, list[str]] = {
|
|
273
|
+
value.lane_id: [] for value in matching_input.lanes
|
|
274
|
+
}
|
|
275
|
+
for edge in matching_input.compatibilities:
|
|
276
|
+
values[edge.lane_id].append(edge.card_key)
|
|
277
|
+
return {key: tuple(value) for key, value in values.items()}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _maximum_cardinality(
|
|
281
|
+
lane_ids: tuple[str, ...],
|
|
282
|
+
available_card_keys: frozenset[str],
|
|
283
|
+
adjacency: dict[str, tuple[str, ...]],
|
|
284
|
+
) -> int:
|
|
285
|
+
"""Return bipartite maximum cardinality for one residual graph."""
|
|
286
|
+
|
|
287
|
+
card_to_lane: dict[str, str] = {}
|
|
288
|
+
|
|
289
|
+
def augment(lane_id: str, seen_cards: set[str]) -> bool:
|
|
290
|
+
for card_key in adjacency[lane_id]:
|
|
291
|
+
if card_key not in available_card_keys or card_key in seen_cards:
|
|
292
|
+
continue
|
|
293
|
+
seen_cards.add(card_key)
|
|
294
|
+
incumbent = card_to_lane.get(card_key)
|
|
295
|
+
if incumbent is None or augment(incumbent, seen_cards):
|
|
296
|
+
card_to_lane[card_key] = lane_id
|
|
297
|
+
return True
|
|
298
|
+
return False
|
|
299
|
+
|
|
300
|
+
for lane_id in lane_ids:
|
|
301
|
+
augment(lane_id, set())
|
|
302
|
+
return len(card_to_lane)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _canonical_maximum_pairs(
|
|
306
|
+
matching_input: LaneCardMatchingInput,
|
|
307
|
+
) -> tuple[tuple[str, str], ...]:
|
|
308
|
+
"""Derive the lexicographically least lane vector at maximum cardinality."""
|
|
309
|
+
|
|
310
|
+
matching_input.__post_init__()
|
|
311
|
+
lane_ids = tuple(value.lane_id for value in matching_input.lanes)
|
|
312
|
+
card_keys = frozenset(value.card_key for value in matching_input.cards)
|
|
313
|
+
adjacency = _adjacency(matching_input)
|
|
314
|
+
target = _maximum_cardinality(lane_ids, card_keys, adjacency)
|
|
315
|
+
used: set[str] = set()
|
|
316
|
+
fixed_cardinality = 0
|
|
317
|
+
selected: list[tuple[str, str]] = []
|
|
318
|
+
|
|
319
|
+
for index, lane_id in enumerate(lane_ids):
|
|
320
|
+
residual_lanes = lane_ids[index + 1 :]
|
|
321
|
+
available = card_keys.difference(used)
|
|
322
|
+
# ``None`` is deliberately last: an earlier lane is matched whenever
|
|
323
|
+
# doing so can retain the globally maximum cardinality.
|
|
324
|
+
candidates: tuple[str | None, ...] = (
|
|
325
|
+
*(value for value in adjacency[lane_id] if value in available),
|
|
326
|
+
None,
|
|
327
|
+
)
|
|
328
|
+
chosen: str | None | object = _NO_CHOICE
|
|
329
|
+
for candidate in candidates:
|
|
330
|
+
next_used = used if candidate is None else used.union((candidate,))
|
|
331
|
+
attainable = fixed_cardinality + (candidate is not None)
|
|
332
|
+
attainable += _maximum_cardinality(
|
|
333
|
+
residual_lanes,
|
|
334
|
+
card_keys.difference(next_used),
|
|
335
|
+
adjacency,
|
|
336
|
+
)
|
|
337
|
+
if attainable == target:
|
|
338
|
+
chosen = candidate
|
|
339
|
+
break
|
|
340
|
+
if chosen is _NO_CHOICE: # pragma: no cover - None always preserves a maximum.
|
|
341
|
+
raise AssertionError("canonical matching construction lost its optimum")
|
|
342
|
+
if type(chosen) is str:
|
|
343
|
+
selected.append((lane_id, chosen))
|
|
344
|
+
used.add(chosen)
|
|
345
|
+
fixed_cardinality += 1
|
|
346
|
+
|
|
347
|
+
if fixed_cardinality != target:
|
|
348
|
+
raise AssertionError("canonical matching differs from maximum cardinality")
|
|
349
|
+
return tuple(selected)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
_NO_CHOICE = object()
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _assignments_for(
|
|
356
|
+
matching_input: LaneCardMatchingInput,
|
|
357
|
+
pairs: tuple[tuple[str, str], ...],
|
|
358
|
+
) -> tuple[LaneCardAssignment, ...]:
|
|
359
|
+
lanes = {value.lane_id: value for value in matching_input.lanes}
|
|
360
|
+
cards = {value.card_key: value for value in matching_input.cards}
|
|
361
|
+
edges = {
|
|
362
|
+
(value.lane_id, value.card_key): value
|
|
363
|
+
for value in matching_input.compatibilities
|
|
364
|
+
}
|
|
365
|
+
return tuple(
|
|
366
|
+
LaneCardAssignment(
|
|
367
|
+
lane=lanes[lane_id],
|
|
368
|
+
card=cards[card_key],
|
|
369
|
+
compatibility_edge_sha256=edges[(lane_id, card_key)].edge_sha256,
|
|
370
|
+
)
|
|
371
|
+
for lane_id, card_key in pairs
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
376
|
+
class LaneCardMatchingReceipt:
|
|
377
|
+
"""Self-validating full or typed-infeasible canonical matching receipt."""
|
|
378
|
+
|
|
379
|
+
matching_input: LaneCardMatchingInput
|
|
380
|
+
status: LaneCardMatchingStatus
|
|
381
|
+
assignments: tuple[LaneCardAssignment, ...]
|
|
382
|
+
unmatched_lanes: tuple[LaneCardMatchingLane, ...]
|
|
383
|
+
unused_cards: tuple[LaneCardMatchingCard, ...]
|
|
384
|
+
maximum_cardinality: int
|
|
385
|
+
required_cardinality: int
|
|
386
|
+
policy_id: str = CANONICAL_LANE_CARD_MATCHING_POLICY_ID
|
|
387
|
+
policy_version: int = CANONICAL_LANE_CARD_MATCHING_POLICY_VERSION
|
|
388
|
+
policy_definition_sha256: str = (
|
|
389
|
+
CANONICAL_LANE_CARD_MATCHING_POLICY_DEFINITION_SHA256
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
def __post_init__(self) -> None:
|
|
393
|
+
if type(self.matching_input) is not LaneCardMatchingInput:
|
|
394
|
+
raise TypeError("matching_input must be an exact LaneCardMatchingInput")
|
|
395
|
+
self.matching_input.__post_init__()
|
|
396
|
+
if type(self.status) is not LaneCardMatchingStatus:
|
|
397
|
+
raise TypeError("status must be an exact LaneCardMatchingStatus")
|
|
398
|
+
if (
|
|
399
|
+
self.policy_id != CANONICAL_LANE_CARD_MATCHING_POLICY_ID
|
|
400
|
+
or self.policy_version != CANONICAL_LANE_CARD_MATCHING_POLICY_VERSION
|
|
401
|
+
or self.policy_definition_sha256
|
|
402
|
+
!= CANONICAL_LANE_CARD_MATCHING_POLICY_DEFINITION_SHA256
|
|
403
|
+
):
|
|
404
|
+
raise ValueError("unsupported lane/card matching policy")
|
|
405
|
+
|
|
406
|
+
expected_pairs = _canonical_maximum_pairs(self.matching_input)
|
|
407
|
+
expected_assignments = _assignments_for(self.matching_input, expected_pairs)
|
|
408
|
+
if self.assignments != expected_assignments:
|
|
409
|
+
raise ValueError("assignments differ from canonical maximum matching")
|
|
410
|
+
assigned_lane_ids = {value.lane.lane_id for value in self.assignments}
|
|
411
|
+
assigned_card_keys = {value.card.card_key for value in self.assignments}
|
|
412
|
+
expected_unmatched = tuple(
|
|
413
|
+
value
|
|
414
|
+
for value in self.matching_input.lanes
|
|
415
|
+
if value.lane_id not in assigned_lane_ids
|
|
416
|
+
)
|
|
417
|
+
expected_unused = tuple(
|
|
418
|
+
value
|
|
419
|
+
for value in self.matching_input.cards
|
|
420
|
+
if value.card_key not in assigned_card_keys
|
|
421
|
+
)
|
|
422
|
+
if self.unmatched_lanes != expected_unmatched:
|
|
423
|
+
raise ValueError("unmatched_lanes differ from canonical maximum matching")
|
|
424
|
+
if self.unused_cards != expected_unused:
|
|
425
|
+
raise ValueError("unused_cards differ from canonical maximum matching")
|
|
426
|
+
expected_maximum = len(expected_assignments)
|
|
427
|
+
expected_required = len(self.matching_input.lanes)
|
|
428
|
+
if (
|
|
429
|
+
type(self.maximum_cardinality) is not int
|
|
430
|
+
or self.maximum_cardinality != expected_maximum
|
|
431
|
+
):
|
|
432
|
+
raise ValueError("maximum_cardinality differs from exact replay")
|
|
433
|
+
if (
|
|
434
|
+
type(self.required_cardinality) is not int
|
|
435
|
+
or self.required_cardinality != expected_required
|
|
436
|
+
):
|
|
437
|
+
raise ValueError("required_cardinality must equal the lane count")
|
|
438
|
+
expected_status = (
|
|
439
|
+
LaneCardMatchingStatus.FULL
|
|
440
|
+
if expected_maximum == expected_required
|
|
441
|
+
else LaneCardMatchingStatus.INFEASIBLE
|
|
442
|
+
)
|
|
443
|
+
if self.status is not expected_status:
|
|
444
|
+
raise ValueError("status differs from exact matching feasibility")
|
|
445
|
+
|
|
446
|
+
@property
|
|
447
|
+
def is_full(self) -> bool:
|
|
448
|
+
return self.status is LaneCardMatchingStatus.FULL
|
|
449
|
+
|
|
450
|
+
@property
|
|
451
|
+
def deficiency(self) -> int:
|
|
452
|
+
return self.required_cardinality - self.maximum_cardinality
|
|
453
|
+
|
|
454
|
+
def assignment_for(self, lane_id: str) -> LaneCardAssignment:
|
|
455
|
+
"""Resolve one match; an infeasible unmatched lane raises ``KeyError``."""
|
|
456
|
+
|
|
457
|
+
self.__post_init__()
|
|
458
|
+
_require_identifier(lane_id, name="lane_id")
|
|
459
|
+
matches = tuple(
|
|
460
|
+
value for value in self.assignments if value.lane.lane_id == lane_id
|
|
461
|
+
)
|
|
462
|
+
if not matches:
|
|
463
|
+
raise KeyError("lane is unmatched or foreign to this receipt")
|
|
464
|
+
return matches[0]
|
|
465
|
+
|
|
466
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
467
|
+
self.__post_init__()
|
|
468
|
+
return {
|
|
469
|
+
"schema_version": 1,
|
|
470
|
+
"policy": {
|
|
471
|
+
"policy_id": self.policy_id,
|
|
472
|
+
"policy_version": self.policy_version,
|
|
473
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
474
|
+
},
|
|
475
|
+
"objective": {
|
|
476
|
+
"primary": "maximum_cardinality",
|
|
477
|
+
"tie_break": "lexicographically_least_lane_ordered_card_vector",
|
|
478
|
+
"unmatched_sorts_after_cards": True,
|
|
479
|
+
"full_requires_every_lane": True,
|
|
480
|
+
},
|
|
481
|
+
"matching_input_sha256": self.matching_input.input_sha256,
|
|
482
|
+
"compatibility_graph_sha256": (
|
|
483
|
+
self.matching_input.compatibility_graph_sha256
|
|
484
|
+
),
|
|
485
|
+
"matching_input": self.matching_input.to_record(),
|
|
486
|
+
"status": self.status.value,
|
|
487
|
+
"maximum_cardinality": self.maximum_cardinality,
|
|
488
|
+
"required_cardinality": self.required_cardinality,
|
|
489
|
+
"deficiency": self.deficiency,
|
|
490
|
+
"assignments": [value.to_record() for value in self.assignments],
|
|
491
|
+
"unmatched_lanes": [
|
|
492
|
+
value.to_record() for value in self.unmatched_lanes
|
|
493
|
+
],
|
|
494
|
+
"unused_cards": [value.to_record() for value in self.unused_cards],
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
@property
|
|
498
|
+
def receipt_sha256(self) -> str:
|
|
499
|
+
return _hash(_RECEIPT_DOMAIN, self._unsigned_record())
|
|
500
|
+
|
|
501
|
+
def to_record(self) -> dict[str, object]:
|
|
502
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
503
|
+
|
|
504
|
+
def __eq__(self, other: object) -> bool:
|
|
505
|
+
return (
|
|
506
|
+
type(other) is LaneCardMatchingReceipt
|
|
507
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
__hash__ = None
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class LaneCardMatchingVerificationError(RuntimeError):
|
|
514
|
+
"""A supplied receipt differs from deterministic canonical replay."""
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
@dataclass(frozen=True, slots=True)
|
|
518
|
+
class CanonicalLaneCardMatchingPlanner:
|
|
519
|
+
"""Build and replay one canonical maximum lane/card matching receipt."""
|
|
520
|
+
|
|
521
|
+
def plan(
|
|
522
|
+
self,
|
|
523
|
+
*,
|
|
524
|
+
lanes: tuple[LaneCardMatchingLane, ...],
|
|
525
|
+
cards: tuple[LaneCardMatchingCard, ...],
|
|
526
|
+
compatibilities: tuple[LaneCardCompatibility, ...],
|
|
527
|
+
) -> LaneCardMatchingReceipt:
|
|
528
|
+
matching_input = LaneCardMatchingInput(
|
|
529
|
+
lanes=lanes,
|
|
530
|
+
cards=cards,
|
|
531
|
+
compatibilities=compatibilities,
|
|
532
|
+
)
|
|
533
|
+
pairs = _canonical_maximum_pairs(matching_input)
|
|
534
|
+
assignments = _assignments_for(matching_input, pairs)
|
|
535
|
+
assigned_lane_ids = {value.lane.lane_id for value in assignments}
|
|
536
|
+
assigned_card_keys = {value.card.card_key for value in assignments}
|
|
537
|
+
maximum = len(assignments)
|
|
538
|
+
required = len(lanes)
|
|
539
|
+
return LaneCardMatchingReceipt(
|
|
540
|
+
matching_input=matching_input,
|
|
541
|
+
status=(
|
|
542
|
+
LaneCardMatchingStatus.FULL
|
|
543
|
+
if maximum == required
|
|
544
|
+
else LaneCardMatchingStatus.INFEASIBLE
|
|
545
|
+
),
|
|
546
|
+
assignments=assignments,
|
|
547
|
+
unmatched_lanes=tuple(
|
|
548
|
+
value for value in lanes if value.lane_id not in assigned_lane_ids
|
|
549
|
+
),
|
|
550
|
+
unused_cards=tuple(
|
|
551
|
+
value for value in cards if value.card_key not in assigned_card_keys
|
|
552
|
+
),
|
|
553
|
+
maximum_cardinality=maximum,
|
|
554
|
+
required_cardinality=required,
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
def replay(
|
|
558
|
+
self,
|
|
559
|
+
receipt: LaneCardMatchingReceipt,
|
|
560
|
+
*,
|
|
561
|
+
lanes: tuple[LaneCardMatchingLane, ...],
|
|
562
|
+
cards: tuple[LaneCardMatchingCard, ...],
|
|
563
|
+
compatibilities: tuple[LaneCardCompatibility, ...],
|
|
564
|
+
) -> LaneCardMatchingReceipt:
|
|
565
|
+
if type(receipt) is not LaneCardMatchingReceipt:
|
|
566
|
+
raise TypeError("receipt must be an exact LaneCardMatchingReceipt")
|
|
567
|
+
receipt.__post_init__()
|
|
568
|
+
expected = self.plan(
|
|
569
|
+
lanes=lanes,
|
|
570
|
+
cards=cards,
|
|
571
|
+
compatibilities=compatibilities,
|
|
572
|
+
)
|
|
573
|
+
if expected.receipt_sha256 != receipt.receipt_sha256:
|
|
574
|
+
raise LaneCardMatchingVerificationError(
|
|
575
|
+
"lane/card matching receipt differs from deterministic replay"
|
|
576
|
+
)
|
|
577
|
+
return expected
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
__all__ = [
|
|
581
|
+
"CANONICAL_LANE_CARD_MATCHING_POLICY_DEFINITION_SHA256",
|
|
582
|
+
"CANONICAL_LANE_CARD_MATCHING_POLICY_ID",
|
|
583
|
+
"CANONICAL_LANE_CARD_MATCHING_POLICY_VERSION",
|
|
584
|
+
"CanonicalLaneCardMatchingPlanner",
|
|
585
|
+
"LaneCardAssignment",
|
|
586
|
+
"LaneCardCompatibility",
|
|
587
|
+
"LaneCardMatchingCard",
|
|
588
|
+
"LaneCardMatchingInput",
|
|
589
|
+
"LaneCardMatchingLane",
|
|
590
|
+
"LaneCardMatchingReceipt",
|
|
591
|
+
"LaneCardMatchingStatus",
|
|
592
|
+
"LaneCardMatchingVerificationError",
|
|
593
|
+
]
|