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,995 @@
|
|
|
1
|
+
"""Domain-neutral operational-tie allocation ports for allocator v3.
|
|
2
|
+
|
|
3
|
+
Allocator v2 intentionally remains an exact-binary64 policy. This parallel
|
|
4
|
+
port makes a benchmark's *decision resolution* explicit: scores whose distance
|
|
5
|
+
from the raw maximum is no larger than an identified, benchmark-supplied gap
|
|
6
|
+
form the operational top set. A treatment-blind, task-keyed public SHA-256
|
|
7
|
+
rank then makes selection replayable without depending on input order,
|
|
8
|
+
concurrent completion order, provider data, forecasts, or outcomes.
|
|
9
|
+
|
|
10
|
+
The allocation-unit key is supplied by the experiment protocol. Cross-arm
|
|
11
|
+
commit ports enforce that every treatment execution in one allocation wave
|
|
12
|
+
uses the same key, seed, resolution, and allocator configuration.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import math
|
|
20
|
+
import re
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from enum import Enum
|
|
23
|
+
|
|
24
|
+
from agent_evolve.domain.patch import require_sha256
|
|
25
|
+
from agent_evolve.ports.action_allocation import (
|
|
26
|
+
ForecastPortfolioUtilityInput,
|
|
27
|
+
ForecastQuantile,
|
|
28
|
+
PortfolioAllocationScore,
|
|
29
|
+
)
|
|
30
|
+
from agent_evolve.ports.action_allocation_frame import (
|
|
31
|
+
FrameActionAllocationRequest,
|
|
32
|
+
FrameActionPortfolioDecision,
|
|
33
|
+
allocation_score_multiset_sha256,
|
|
34
|
+
validate_frame_action_portfolio_decision,
|
|
35
|
+
)
|
|
36
|
+
from agent_evolve.ports.action_forecast import ResolvedActionForecast
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
40
|
+
_CANDIDATE_LABEL = re.compile(r"^row_[0-9]{8}$")
|
|
41
|
+
_MAX_ALLOCATION_UNIT_KEY_BYTES = 512
|
|
42
|
+
_UINT64_MAX = (1 << 64) - 1
|
|
43
|
+
|
|
44
|
+
GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_ID = "greedy_risk_diversity"
|
|
45
|
+
GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_VERSION = 3
|
|
46
|
+
GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_DEFINITION_SHA256 = hashlib.sha256(
|
|
47
|
+
b"agent-evolve:greedy-risk-diversity:v3:"
|
|
48
|
+
b"v2-p10-p50-p90-risk-and-attainable-family-diversity-score;"
|
|
49
|
+
b"operational-top-is-raw-max-minus-candidate-inclusive-less-than-or-equal-"
|
|
50
|
+
b"benchmark-identified-maximum-indistinguishable-gap;"
|
|
51
|
+
b"rank=sha256(public-uint64-seed,common-allocation-unit-key,step,"
|
|
52
|
+
b"option-identity);select-smallest-rank;"
|
|
53
|
+
b"seed-sampling-law-and-provenance-bound;"
|
|
54
|
+
b"fixed-seed-is-point-mass-with-no-reference-weight;"
|
|
55
|
+
b"uniform-uint64-has-only-random-oracle-prior-reference;"
|
|
56
|
+
b"modes=public-hash-rank-or-fail-closed"
|
|
57
|
+
).hexdigest()
|
|
58
|
+
|
|
59
|
+
_RESOLUTION_DOMAIN = b"agent-evolve:allocation-score-resolution:v1\x00"
|
|
60
|
+
_SELECTION_DOMAIN = b"agent-evolve:allocation-v3-selection-binding:v1\x00"
|
|
61
|
+
_PUBLIC_RANK_DOMAIN = b"agent-evolve:allocation-v3-public-rank:v1\x00"
|
|
62
|
+
_CONFIGURATION_DOMAIN = b"agent-evolve:greedy-risk-diversity-v3-config:v1\x00"
|
|
63
|
+
_REQUEST_DOMAIN = b"agent-evolve:operational-frame-allocation-request:v1\x00"
|
|
64
|
+
_CANDIDATE_DOMAIN = b"agent-evolve:allocation-v3-candidate:v1\x00"
|
|
65
|
+
_STEP_DOMAIN = b"agent-evolve:allocation-v3-step-audit:v1\x00"
|
|
66
|
+
_AUDIT_DOMAIN = b"agent-evolve:allocation-v3-audit:v1\x00"
|
|
67
|
+
_RESULT_DOMAIN = b"agent-evolve:operational-frame-allocation-result:v1\x00"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _canonical_json(value: object) -> bytes:
|
|
71
|
+
return json.dumps(
|
|
72
|
+
value,
|
|
73
|
+
allow_nan=False,
|
|
74
|
+
ensure_ascii=True,
|
|
75
|
+
separators=(",", ":"),
|
|
76
|
+
sort_keys=True,
|
|
77
|
+
).encode("ascii")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
81
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _finite_float(value: object, name: str) -> float:
|
|
85
|
+
if type(value) is not float or not math.isfinite(value):
|
|
86
|
+
raise TypeError(f"{name} must be a finite canonical float")
|
|
87
|
+
return value
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _token(value: object, name: str) -> str:
|
|
91
|
+
if type(value) is not str or _TOKEN.fullmatch(value) is None:
|
|
92
|
+
raise ValueError(f"{name} must use the closed lowercase token grammar")
|
|
93
|
+
return value
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _frame(value: bytes) -> bytes:
|
|
97
|
+
return len(value).to_bytes(8, "big", signed=False) + value
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class AllocationV3TieMode(str, Enum):
|
|
101
|
+
"""Prospectively selected handling of an operational top set."""
|
|
102
|
+
|
|
103
|
+
PUBLIC_HASH_RANK = "public_hash_rank"
|
|
104
|
+
FAIL_CLOSED = "fail_closed"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class AllocationV3SeedSamplingLaw(str, Enum):
|
|
108
|
+
"""Prospectively declared provenance law for the public uint64 seed."""
|
|
109
|
+
|
|
110
|
+
FIXED_PUBLIC = "fixed_public"
|
|
111
|
+
UNIFORM_UINT64 = "uniform_uint64"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
115
|
+
class AllocationScoreResolutionBinding:
|
|
116
|
+
"""Identified benchmark declaration of decision-score resolution.
|
|
117
|
+
|
|
118
|
+
The value is not a universal floating-point epsilon. It is the maximum
|
|
119
|
+
raw marginal-utility difference the benchmark declares scientifically
|
|
120
|
+
indistinguishable for this allocation protocol.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
policy_id: str
|
|
124
|
+
policy_version: int
|
|
125
|
+
policy_definition_sha256: str
|
|
126
|
+
maximum_indistinguishable_score_gap: float
|
|
127
|
+
|
|
128
|
+
def __post_init__(self) -> None:
|
|
129
|
+
_token(self.policy_id, "policy_id")
|
|
130
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
131
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
132
|
+
require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
|
|
133
|
+
gap = _finite_float(
|
|
134
|
+
self.maximum_indistinguishable_score_gap,
|
|
135
|
+
"maximum_indistinguishable_score_gap",
|
|
136
|
+
)
|
|
137
|
+
if gap < 0.0:
|
|
138
|
+
raise ValueError("maximum_indistinguishable_score_gap cannot be negative")
|
|
139
|
+
|
|
140
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
141
|
+
self.__post_init__()
|
|
142
|
+
return {
|
|
143
|
+
"schema_version": 1,
|
|
144
|
+
"policy_id": self.policy_id,
|
|
145
|
+
"policy_version": self.policy_version,
|
|
146
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
147
|
+
"maximum_indistinguishable_score_gap_hex": (
|
|
148
|
+
self.maximum_indistinguishable_score_gap.hex()
|
|
149
|
+
),
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def binding_sha256(self) -> str:
|
|
154
|
+
return _hash(_RESOLUTION_DOMAIN, self._unsigned_record())
|
|
155
|
+
|
|
156
|
+
def to_record(self) -> dict[str, object]:
|
|
157
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
158
|
+
|
|
159
|
+
def __eq__(self, other: object) -> bool:
|
|
160
|
+
return (
|
|
161
|
+
type(other) is AllocationScoreResolutionBinding
|
|
162
|
+
and self.binding_sha256 == other.binding_sha256
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
__hash__ = None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
169
|
+
class AllocationV3SelectionBinding:
|
|
170
|
+
"""Public treatment-blind task key and mode for operational ties."""
|
|
171
|
+
|
|
172
|
+
policy_id: str
|
|
173
|
+
policy_version: int
|
|
174
|
+
policy_definition_sha256: str
|
|
175
|
+
mode: AllocationV3TieMode
|
|
176
|
+
seed_sampling_law: AllocationV3SeedSamplingLaw
|
|
177
|
+
seed_provenance_sha256: str
|
|
178
|
+
public_seed: int
|
|
179
|
+
allocation_unit_key: str
|
|
180
|
+
|
|
181
|
+
def __post_init__(self) -> None:
|
|
182
|
+
_token(self.policy_id, "policy_id")
|
|
183
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
184
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
185
|
+
require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
|
|
186
|
+
if type(self.mode) is not AllocationV3TieMode:
|
|
187
|
+
raise TypeError("mode must be an exact AllocationV3TieMode")
|
|
188
|
+
if type(self.seed_sampling_law) is not AllocationV3SeedSamplingLaw:
|
|
189
|
+
raise TypeError(
|
|
190
|
+
"seed_sampling_law must be an exact AllocationV3SeedSamplingLaw"
|
|
191
|
+
)
|
|
192
|
+
require_sha256(self.seed_provenance_sha256, "seed_provenance_sha256")
|
|
193
|
+
if (
|
|
194
|
+
type(self.public_seed) is not int
|
|
195
|
+
or not 0 <= self.public_seed <= _UINT64_MAX
|
|
196
|
+
):
|
|
197
|
+
raise ValueError("public_seed must be an exact unsigned 64-bit integer")
|
|
198
|
+
if (
|
|
199
|
+
type(self.allocation_unit_key) is not str
|
|
200
|
+
or not self.allocation_unit_key
|
|
201
|
+
or "\x00" in self.allocation_unit_key
|
|
202
|
+
):
|
|
203
|
+
raise ValueError("allocation_unit_key must be non-empty and NUL-free")
|
|
204
|
+
try:
|
|
205
|
+
encoded = self.allocation_unit_key.encode("utf-8", errors="strict")
|
|
206
|
+
except UnicodeEncodeError as exc:
|
|
207
|
+
raise ValueError("allocation_unit_key must be strict UTF-8") from exc
|
|
208
|
+
if len(encoded) > _MAX_ALLOCATION_UNIT_KEY_BYTES:
|
|
209
|
+
raise ValueError("allocation_unit_key exceeds its byte limit")
|
|
210
|
+
|
|
211
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
212
|
+
self.__post_init__()
|
|
213
|
+
return {
|
|
214
|
+
"schema_version": 1,
|
|
215
|
+
"policy_id": self.policy_id,
|
|
216
|
+
"policy_version": self.policy_version,
|
|
217
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
218
|
+
"mode": self.mode.value,
|
|
219
|
+
"seed_sampling_law": self.seed_sampling_law.value,
|
|
220
|
+
"seed_provenance_sha256": self.seed_provenance_sha256,
|
|
221
|
+
"public_seed_uint64": self.public_seed,
|
|
222
|
+
"allocation_unit_key": self.allocation_unit_key,
|
|
223
|
+
"rank_inputs_excluded": [
|
|
224
|
+
"treatment_identity",
|
|
225
|
+
"forecast_values",
|
|
226
|
+
"provider_identity",
|
|
227
|
+
"completion_order",
|
|
228
|
+
"outcomes",
|
|
229
|
+
],
|
|
230
|
+
"selection_probability_claim": (
|
|
231
|
+
"point_mass_after_fixed_public_seed_no_reference_weight"
|
|
232
|
+
if self.seed_sampling_law
|
|
233
|
+
is AllocationV3SeedSamplingLaw.FIXED_PUBLIC
|
|
234
|
+
else "random_oracle_prior_reference_not_conditional_propensity"
|
|
235
|
+
),
|
|
236
|
+
"seed_provenance_boundary": {
|
|
237
|
+
"digest_alone_proves_pre_forecast_preregistration": False,
|
|
238
|
+
"chronology_requires_external_durable_release": True,
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def binding_sha256(self) -> str:
|
|
244
|
+
return _hash(_SELECTION_DOMAIN, self._unsigned_record())
|
|
245
|
+
|
|
246
|
+
def public_rank_sha256(self, *, step: int, option_identity_sha256: str) -> str:
|
|
247
|
+
"""Return the public counter-based SHA rank for one step/option.
|
|
248
|
+
|
|
249
|
+
The signature deliberately has no treatment, forecast, provider,
|
|
250
|
+
completion, or outcome argument.
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
self.__post_init__()
|
|
254
|
+
if type(step) is not int or step <= 0:
|
|
255
|
+
raise ValueError("step must be a positive exact integer")
|
|
256
|
+
require_sha256(option_identity_sha256, "option_identity_sha256")
|
|
257
|
+
digest = hashlib.sha256()
|
|
258
|
+
digest.update(_PUBLIC_RANK_DOMAIN)
|
|
259
|
+
digest.update(self.public_seed.to_bytes(8, "big", signed=False))
|
|
260
|
+
digest.update(_frame(self.allocation_unit_key.encode("utf-8")))
|
|
261
|
+
digest.update(step.to_bytes(8, "big", signed=False))
|
|
262
|
+
digest.update(bytes.fromhex(option_identity_sha256))
|
|
263
|
+
return digest.hexdigest()
|
|
264
|
+
|
|
265
|
+
def to_record(self) -> dict[str, object]:
|
|
266
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
267
|
+
|
|
268
|
+
def __eq__(self, other: object) -> bool:
|
|
269
|
+
return (
|
|
270
|
+
type(other) is AllocationV3SelectionBinding
|
|
271
|
+
and self.binding_sha256 == other.binding_sha256
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
__hash__ = None
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
278
|
+
class OperationalFrameActionAllocationRequest:
|
|
279
|
+
"""Exact v3 configuration composed around an authenticated frame request."""
|
|
280
|
+
|
|
281
|
+
allocation: FrameActionAllocationRequest = field(repr=False, compare=False)
|
|
282
|
+
risk_aversion: float
|
|
283
|
+
diversity_weight: float
|
|
284
|
+
score_resolution: AllocationScoreResolutionBinding
|
|
285
|
+
tie_selection: AllocationV3SelectionBinding
|
|
286
|
+
|
|
287
|
+
def __post_init__(self) -> None:
|
|
288
|
+
if type(self.allocation) is not FrameActionAllocationRequest:
|
|
289
|
+
raise TypeError("allocation must be an exact frame allocation request")
|
|
290
|
+
self.allocation.__post_init__()
|
|
291
|
+
for name in ("risk_aversion", "diversity_weight"):
|
|
292
|
+
value = _finite_float(getattr(self, name), name)
|
|
293
|
+
if value < 0.0:
|
|
294
|
+
raise ValueError(f"{name} must be non-negative")
|
|
295
|
+
if type(self.score_resolution) is not AllocationScoreResolutionBinding:
|
|
296
|
+
raise TypeError("score_resolution must be an exact identified binding")
|
|
297
|
+
self.score_resolution.__post_init__()
|
|
298
|
+
if type(self.tie_selection) is not AllocationV3SelectionBinding:
|
|
299
|
+
raise TypeError("tie_selection must be an exact identified binding")
|
|
300
|
+
self.tie_selection.__post_init__()
|
|
301
|
+
|
|
302
|
+
def _configuration_record(self) -> dict[str, object]:
|
|
303
|
+
self.__post_init__()
|
|
304
|
+
return {
|
|
305
|
+
"allocator_policy_id": GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_ID,
|
|
306
|
+
"allocator_policy_version": GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_VERSION,
|
|
307
|
+
"allocator_definition_sha256": (
|
|
308
|
+
GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_DEFINITION_SHA256
|
|
309
|
+
),
|
|
310
|
+
"risk_aversion_hex": self.risk_aversion.hex(),
|
|
311
|
+
"diversity_weight_hex": self.diversity_weight.hex(),
|
|
312
|
+
"score_resolution": self.score_resolution.to_record(),
|
|
313
|
+
"tie_selection": self.tie_selection.to_record(),
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def allocator_configuration_sha256(self) -> str:
|
|
318
|
+
return _hash(_CONFIGURATION_DOMAIN, self._configuration_record())
|
|
319
|
+
|
|
320
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
321
|
+
self.__post_init__()
|
|
322
|
+
return {
|
|
323
|
+
"schema_version": 1,
|
|
324
|
+
"base_allocation_request_sha256": self.allocation.request_sha256,
|
|
325
|
+
"frame_receipt_sha256": self.allocation.frame.receipt_sha256,
|
|
326
|
+
"source_forecast_receipt_sha256": (
|
|
327
|
+
self.allocation.frame.source_forecast_receipt_sha256
|
|
328
|
+
),
|
|
329
|
+
"eligible_options_sha256": self.allocation.eligible_options_sha256,
|
|
330
|
+
"portfolio_size": self.allocation.portfolio_size,
|
|
331
|
+
"allocator_configuration": self._configuration_record(),
|
|
332
|
+
"allocator_configuration_sha256": (
|
|
333
|
+
self.allocator_configuration_sha256
|
|
334
|
+
),
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@property
|
|
338
|
+
def request_sha256(self) -> str:
|
|
339
|
+
return _hash(_REQUEST_DOMAIN, self._unsigned_record())
|
|
340
|
+
|
|
341
|
+
def to_record(self) -> dict[str, object]:
|
|
342
|
+
return {**self._unsigned_record(), "request_sha256": self.request_sha256}
|
|
343
|
+
|
|
344
|
+
def __eq__(self, other: object) -> bool:
|
|
345
|
+
return (
|
|
346
|
+
type(other) is OperationalFrameActionAllocationRequest
|
|
347
|
+
and self.request_sha256 == other.request_sha256
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
__hash__ = None
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
354
|
+
class AllocationV3Candidate:
|
|
355
|
+
"""One complete marginal-score row and its public task-keyed rank."""
|
|
356
|
+
|
|
357
|
+
candidate_label: str
|
|
358
|
+
option_identity_sha256: str
|
|
359
|
+
score: PortfolioAllocationScore
|
|
360
|
+
marginal_total_utility: float
|
|
361
|
+
public_rank_sha256: str
|
|
362
|
+
|
|
363
|
+
def __post_init__(self) -> None:
|
|
364
|
+
if (
|
|
365
|
+
type(self.candidate_label) is not str
|
|
366
|
+
or _CANDIDATE_LABEL.fullmatch(self.candidate_label) is None
|
|
367
|
+
):
|
|
368
|
+
raise ValueError("candidate_label must be an opaque global-row label")
|
|
369
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
370
|
+
if type(self.score) is not PortfolioAllocationScore:
|
|
371
|
+
raise TypeError("score must be an exact PortfolioAllocationScore")
|
|
372
|
+
self.score.__post_init__()
|
|
373
|
+
primary = self.score.p50_utility - self.score.risk_penalty
|
|
374
|
+
total = primary + self.score.diversity_reward
|
|
375
|
+
if not math.isfinite(primary) or not math.isfinite(total):
|
|
376
|
+
raise ValueError("candidate score composition became non-finite")
|
|
377
|
+
if self.score.total_utility != total:
|
|
378
|
+
raise ValueError(
|
|
379
|
+
"candidate total must equal primary risk-adjusted utility "
|
|
380
|
+
"plus diversity"
|
|
381
|
+
)
|
|
382
|
+
_finite_float(self.marginal_total_utility, "marginal_total_utility")
|
|
383
|
+
require_sha256(self.public_rank_sha256, "public_rank_sha256")
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def primary_risk_adjusted_utility(self) -> float:
|
|
387
|
+
"""Risk-adjusted utility before any additive diversity composition."""
|
|
388
|
+
|
|
389
|
+
return self.score.p50_utility - self.score.risk_penalty
|
|
390
|
+
|
|
391
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
392
|
+
self.__post_init__()
|
|
393
|
+
return {
|
|
394
|
+
"schema_version": 1,
|
|
395
|
+
"candidate_label": self.candidate_label,
|
|
396
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
397
|
+
"score": self.score.to_record(),
|
|
398
|
+
"primary_risk_adjusted_utility_hex": (
|
|
399
|
+
self.primary_risk_adjusted_utility.hex()
|
|
400
|
+
),
|
|
401
|
+
"diversity_reward_hex": self.score.diversity_reward.hex(),
|
|
402
|
+
"marginal_total_utility_hex": self.marginal_total_utility.hex(),
|
|
403
|
+
"public_rank_sha256": self.public_rank_sha256,
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
@property
|
|
407
|
+
def receipt_sha256(self) -> str:
|
|
408
|
+
return _hash(_CANDIDATE_DOMAIN, self._unsigned_record())
|
|
409
|
+
|
|
410
|
+
def to_record(self) -> dict[str, object]:
|
|
411
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
412
|
+
|
|
413
|
+
def __eq__(self, other: object) -> bool:
|
|
414
|
+
return (
|
|
415
|
+
type(other) is AllocationV3Candidate
|
|
416
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
__hash__ = None
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def allocation_v3_failure_codes(
|
|
423
|
+
*,
|
|
424
|
+
mode: AllocationV3TieMode,
|
|
425
|
+
operational_top_count: int,
|
|
426
|
+
) -> tuple[str, ...]:
|
|
427
|
+
if type(mode) is not AllocationV3TieMode:
|
|
428
|
+
raise TypeError("mode must be an exact AllocationV3TieMode")
|
|
429
|
+
if type(operational_top_count) is not int or operational_top_count <= 0:
|
|
430
|
+
raise ValueError("operational_top_count must be a positive exact integer")
|
|
431
|
+
if mode is AllocationV3TieMode.FAIL_CLOSED and operational_top_count > 1:
|
|
432
|
+
return ("operational_tie_fail_closed",)
|
|
433
|
+
return ()
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
437
|
+
class AllocationV3StepAudit:
|
|
438
|
+
"""Complete replayable candidate table and v3 selection facts for one step."""
|
|
439
|
+
|
|
440
|
+
step: int
|
|
441
|
+
candidates: tuple[AllocationV3Candidate, ...]
|
|
442
|
+
distinct_finite_score_count: int
|
|
443
|
+
raw_top_score: float
|
|
444
|
+
raw_runner_gap: float
|
|
445
|
+
raw_top_candidate_labels: tuple[str, ...]
|
|
446
|
+
operational_top_candidate_labels: tuple[str, ...]
|
|
447
|
+
selected_candidate_label: str
|
|
448
|
+
selected_public_rank_sha256: str
|
|
449
|
+
random_oracle_prior_weight_numerator: int | None
|
|
450
|
+
random_oracle_prior_weight_denominator: int | None
|
|
451
|
+
score_multiset_sha256: str
|
|
452
|
+
failure_codes: tuple[str, ...]
|
|
453
|
+
passes: bool
|
|
454
|
+
|
|
455
|
+
def __post_init__(self) -> None:
|
|
456
|
+
if type(self.step) is not int or self.step <= 0:
|
|
457
|
+
raise ValueError("step must be a positive exact integer")
|
|
458
|
+
if type(self.candidates) is not tuple or not self.candidates or any(
|
|
459
|
+
type(value) is not AllocationV3Candidate for value in self.candidates
|
|
460
|
+
):
|
|
461
|
+
raise ValueError("candidates must be a non-empty exact candidate tuple")
|
|
462
|
+
for value in self.candidates:
|
|
463
|
+
value.__post_init__()
|
|
464
|
+
labels = tuple(value.candidate_label for value in self.candidates)
|
|
465
|
+
if labels != tuple(sorted(labels)) or len(set(labels)) != len(labels):
|
|
466
|
+
raise ValueError("candidate rows must be unique and label-canonical")
|
|
467
|
+
identities = tuple(value.option_identity_sha256 for value in self.candidates)
|
|
468
|
+
if len(set(identities)) != len(identities):
|
|
469
|
+
raise ValueError("candidate option identities must be unique")
|
|
470
|
+
ranks = tuple(value.public_rank_sha256 for value in self.candidates)
|
|
471
|
+
if len(set(ranks)) != len(ranks):
|
|
472
|
+
raise ValueError("public SHA ranks must be unique")
|
|
473
|
+
if (
|
|
474
|
+
type(self.distinct_finite_score_count) is not int
|
|
475
|
+
or not 1 <= self.distinct_finite_score_count <= len(self.candidates)
|
|
476
|
+
):
|
|
477
|
+
raise ValueError("distinct_finite_score_count is outside candidate count")
|
|
478
|
+
_finite_float(self.raw_top_score, "raw_top_score")
|
|
479
|
+
gap = _finite_float(self.raw_runner_gap, "raw_runner_gap")
|
|
480
|
+
if gap < 0.0:
|
|
481
|
+
raise ValueError("raw_runner_gap cannot be negative")
|
|
482
|
+
label_set = set(labels)
|
|
483
|
+
for name in (
|
|
484
|
+
"raw_top_candidate_labels",
|
|
485
|
+
"operational_top_candidate_labels",
|
|
486
|
+
):
|
|
487
|
+
values = getattr(self, name)
|
|
488
|
+
if (
|
|
489
|
+
type(values) is not tuple
|
|
490
|
+
or not values
|
|
491
|
+
or any(
|
|
492
|
+
type(value) is not str
|
|
493
|
+
or _CANDIDATE_LABEL.fullmatch(value) is None
|
|
494
|
+
for value in values
|
|
495
|
+
)
|
|
496
|
+
or values != tuple(sorted(set(values)))
|
|
497
|
+
or not set(values).issubset(label_set)
|
|
498
|
+
):
|
|
499
|
+
raise ValueError(f"{name} must be a non-empty canonical subset")
|
|
500
|
+
if not set(self.raw_top_candidate_labels).issubset(
|
|
501
|
+
self.operational_top_candidate_labels
|
|
502
|
+
):
|
|
503
|
+
raise ValueError("raw top candidates must belong to the operational top")
|
|
504
|
+
if self.selected_candidate_label not in self.operational_top_candidate_labels:
|
|
505
|
+
raise ValueError("selected candidate must belong to the operational top")
|
|
506
|
+
require_sha256(
|
|
507
|
+
self.selected_public_rank_sha256,
|
|
508
|
+
"selected_public_rank_sha256",
|
|
509
|
+
)
|
|
510
|
+
if (self.random_oracle_prior_weight_numerator is None) is not (
|
|
511
|
+
self.random_oracle_prior_weight_denominator is None
|
|
512
|
+
):
|
|
513
|
+
raise ValueError("random-oracle prior weight fields must be paired")
|
|
514
|
+
if self.random_oracle_prior_weight_numerator is not None:
|
|
515
|
+
if (
|
|
516
|
+
type(self.random_oracle_prior_weight_numerator) is not int
|
|
517
|
+
or self.random_oracle_prior_weight_numerator != 1
|
|
518
|
+
):
|
|
519
|
+
raise ValueError(
|
|
520
|
+
"random-oracle prior weight numerator must be exactly one"
|
|
521
|
+
)
|
|
522
|
+
if (
|
|
523
|
+
type(self.random_oracle_prior_weight_denominator) is not int
|
|
524
|
+
or self.random_oracle_prior_weight_denominator
|
|
525
|
+
!= len(self.operational_top_candidate_labels)
|
|
526
|
+
):
|
|
527
|
+
raise ValueError(
|
|
528
|
+
"random-oracle prior weight must be 1/|operational top|"
|
|
529
|
+
)
|
|
530
|
+
require_sha256(self.score_multiset_sha256, "score_multiset_sha256")
|
|
531
|
+
if (
|
|
532
|
+
type(self.failure_codes) is not tuple
|
|
533
|
+
or any(
|
|
534
|
+
type(value) is not str or _TOKEN.fullmatch(value) is None
|
|
535
|
+
for value in self.failure_codes
|
|
536
|
+
)
|
|
537
|
+
or self.failure_codes != tuple(sorted(set(self.failure_codes)))
|
|
538
|
+
):
|
|
539
|
+
raise ValueError("failure_codes must be unique canonical tokens")
|
|
540
|
+
if type(self.passes) is not bool or self.passes is not (not self.failure_codes):
|
|
541
|
+
raise ValueError("passes must be the inverse of failure_codes")
|
|
542
|
+
|
|
543
|
+
@property
|
|
544
|
+
def candidate_count(self) -> int:
|
|
545
|
+
return len(self.candidates)
|
|
546
|
+
|
|
547
|
+
@property
|
|
548
|
+
def raw_top_tie_count(self) -> int:
|
|
549
|
+
return len(self.raw_top_candidate_labels)
|
|
550
|
+
|
|
551
|
+
@property
|
|
552
|
+
def operational_top_count(self) -> int:
|
|
553
|
+
return len(self.operational_top_candidate_labels)
|
|
554
|
+
|
|
555
|
+
@property
|
|
556
|
+
def random_oracle_prior_seed_law_reference_weight(self) -> str | None:
|
|
557
|
+
if self.random_oracle_prior_weight_denominator is None:
|
|
558
|
+
return None
|
|
559
|
+
return f"1/{self.random_oracle_prior_weight_denominator}"
|
|
560
|
+
|
|
561
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
562
|
+
self.__post_init__()
|
|
563
|
+
return {
|
|
564
|
+
"schema_version": 1,
|
|
565
|
+
"step": self.step,
|
|
566
|
+
"candidate_count": self.candidate_count,
|
|
567
|
+
"distinct_finite_score_count": self.distinct_finite_score_count,
|
|
568
|
+
"candidates": [value.to_record() for value in self.candidates],
|
|
569
|
+
"raw_top_score_hex": self.raw_top_score.hex(),
|
|
570
|
+
"raw_runner_gap_hex": self.raw_runner_gap.hex(),
|
|
571
|
+
"raw_top_tie_count": self.raw_top_tie_count,
|
|
572
|
+
"raw_top_candidate_labels": list(self.raw_top_candidate_labels),
|
|
573
|
+
"operational_top_count": self.operational_top_count,
|
|
574
|
+
"operational_top_candidate_labels": list(
|
|
575
|
+
self.operational_top_candidate_labels
|
|
576
|
+
),
|
|
577
|
+
"selected_candidate_label": self.selected_candidate_label,
|
|
578
|
+
"selected_public_rank_sha256": self.selected_public_rank_sha256,
|
|
579
|
+
"random_oracle_prior_seed_law_reference_weight": (
|
|
580
|
+
None
|
|
581
|
+
if self.random_oracle_prior_weight_numerator is None
|
|
582
|
+
else {
|
|
583
|
+
"numerator": self.random_oracle_prior_weight_numerator,
|
|
584
|
+
"denominator": self.random_oracle_prior_weight_denominator,
|
|
585
|
+
"ratio": (
|
|
586
|
+
self.random_oracle_prior_seed_law_reference_weight
|
|
587
|
+
),
|
|
588
|
+
"interpretation": (
|
|
589
|
+
"prior_reference_under_uniform_uint64_random_oracle_model"
|
|
590
|
+
),
|
|
591
|
+
"is_conditional_propensity": False,
|
|
592
|
+
}
|
|
593
|
+
),
|
|
594
|
+
"score_multiset_sha256": self.score_multiset_sha256,
|
|
595
|
+
"failure_codes": list(self.failure_codes),
|
|
596
|
+
"passes": self.passes,
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
@property
|
|
600
|
+
def receipt_sha256(self) -> str:
|
|
601
|
+
return _hash(_STEP_DOMAIN, self._unsigned_record())
|
|
602
|
+
|
|
603
|
+
def to_record(self) -> dict[str, object]:
|
|
604
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
605
|
+
|
|
606
|
+
def __eq__(self, other: object) -> bool:
|
|
607
|
+
return (
|
|
608
|
+
type(other) is AllocationV3StepAudit
|
|
609
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
__hash__ = None
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def _validate_step_against_policy(
|
|
616
|
+
step: AllocationV3StepAudit,
|
|
617
|
+
*,
|
|
618
|
+
resolution: AllocationScoreResolutionBinding,
|
|
619
|
+
selection: AllocationV3SelectionBinding,
|
|
620
|
+
) -> None:
|
|
621
|
+
step.__post_init__()
|
|
622
|
+
resolution.__post_init__()
|
|
623
|
+
selection.__post_init__()
|
|
624
|
+
for candidate in step.candidates:
|
|
625
|
+
expected_rank = selection.public_rank_sha256(
|
|
626
|
+
step=step.step,
|
|
627
|
+
option_identity_sha256=candidate.option_identity_sha256,
|
|
628
|
+
)
|
|
629
|
+
if candidate.public_rank_sha256 != expected_rank:
|
|
630
|
+
raise ValueError("candidate public rank differs from the bound task key")
|
|
631
|
+
|
|
632
|
+
scores = tuple(value.marginal_total_utility for value in step.candidates)
|
|
633
|
+
raw_top = max(scores)
|
|
634
|
+
ranked = sorted(scores, reverse=True)
|
|
635
|
+
raw_runner_gap = 0.0 if len(ranked) == 1 else ranked[0] - ranked[1]
|
|
636
|
+
if not math.isfinite(raw_runner_gap):
|
|
637
|
+
raise ValueError("raw runner-gap arithmetic became non-finite")
|
|
638
|
+
raw_labels = tuple(
|
|
639
|
+
value.candidate_label
|
|
640
|
+
for value in step.candidates
|
|
641
|
+
if value.marginal_total_utility == raw_top
|
|
642
|
+
)
|
|
643
|
+
operational = tuple(
|
|
644
|
+
value
|
|
645
|
+
for value in step.candidates
|
|
646
|
+
if raw_top - value.marginal_total_utility
|
|
647
|
+
<= resolution.maximum_indistinguishable_score_gap
|
|
648
|
+
)
|
|
649
|
+
operational_labels = tuple(value.candidate_label for value in operational)
|
|
650
|
+
selected = min(
|
|
651
|
+
operational,
|
|
652
|
+
key=lambda value: (value.public_rank_sha256, value.candidate_label),
|
|
653
|
+
)
|
|
654
|
+
expected_failures = allocation_v3_failure_codes(
|
|
655
|
+
mode=selection.mode,
|
|
656
|
+
operational_top_count=len(operational),
|
|
657
|
+
)
|
|
658
|
+
if selection.seed_sampling_law is AllocationV3SeedSamplingLaw.FIXED_PUBLIC:
|
|
659
|
+
if (
|
|
660
|
+
step.random_oracle_prior_weight_numerator is not None
|
|
661
|
+
or step.random_oracle_prior_weight_denominator is not None
|
|
662
|
+
):
|
|
663
|
+
raise ValueError("fixed public seeds cannot carry a reference weight")
|
|
664
|
+
elif (
|
|
665
|
+
step.random_oracle_prior_weight_numerator != 1
|
|
666
|
+
or step.random_oracle_prior_weight_denominator != len(operational)
|
|
667
|
+
):
|
|
668
|
+
raise ValueError(
|
|
669
|
+
"uniform uint64 seeds require the random-oracle prior reference"
|
|
670
|
+
)
|
|
671
|
+
if step.distinct_finite_score_count != len(set(scores)):
|
|
672
|
+
raise ValueError("distinct score count differs from the candidate table")
|
|
673
|
+
if step.raw_top_score != raw_top:
|
|
674
|
+
raise ValueError("raw top score differs from the candidate table")
|
|
675
|
+
if step.raw_runner_gap != raw_runner_gap:
|
|
676
|
+
raise ValueError("raw runner gap differs from the candidate table")
|
|
677
|
+
if step.raw_top_candidate_labels != raw_labels:
|
|
678
|
+
raise ValueError("raw top labels differ from exact binary64 ties")
|
|
679
|
+
if step.operational_top_candidate_labels != operational_labels:
|
|
680
|
+
raise ValueError("operational top is not the inclusive raw-max-relative set")
|
|
681
|
+
if (
|
|
682
|
+
step.selected_candidate_label != selected.candidate_label
|
|
683
|
+
or step.selected_public_rank_sha256 != selected.public_rank_sha256
|
|
684
|
+
):
|
|
685
|
+
raise ValueError("selected candidate is not the smallest public SHA rank")
|
|
686
|
+
if step.score_multiset_sha256 != allocation_score_multiset_sha256(scores):
|
|
687
|
+
raise ValueError("score multiset receipt differs from the candidate table")
|
|
688
|
+
if step.failure_codes != expected_failures:
|
|
689
|
+
raise ValueError("step failures differ from the v3 tie mode")
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
693
|
+
class OperationalFrameActionAllocationAudit:
|
|
694
|
+
operational_request_sha256: str
|
|
695
|
+
base_allocation_request_sha256: str
|
|
696
|
+
decision_receipt_sha256: str
|
|
697
|
+
frame_receipt_sha256: str
|
|
698
|
+
score_resolution: AllocationScoreResolutionBinding
|
|
699
|
+
tie_selection: AllocationV3SelectionBinding
|
|
700
|
+
steps: tuple[AllocationV3StepAudit, ...]
|
|
701
|
+
candidate_score_count: int
|
|
702
|
+
passes: bool
|
|
703
|
+
|
|
704
|
+
def __post_init__(self) -> None:
|
|
705
|
+
for name in (
|
|
706
|
+
"operational_request_sha256",
|
|
707
|
+
"base_allocation_request_sha256",
|
|
708
|
+
"decision_receipt_sha256",
|
|
709
|
+
"frame_receipt_sha256",
|
|
710
|
+
):
|
|
711
|
+
require_sha256(getattr(self, name), name)
|
|
712
|
+
if type(self.score_resolution) is not AllocationScoreResolutionBinding:
|
|
713
|
+
raise TypeError("score_resolution must be an exact binding")
|
|
714
|
+
self.score_resolution.__post_init__()
|
|
715
|
+
if type(self.tie_selection) is not AllocationV3SelectionBinding:
|
|
716
|
+
raise TypeError("tie_selection must be an exact binding")
|
|
717
|
+
self.tie_selection.__post_init__()
|
|
718
|
+
if type(self.steps) is not tuple or not self.steps or any(
|
|
719
|
+
type(value) is not AllocationV3StepAudit for value in self.steps
|
|
720
|
+
):
|
|
721
|
+
raise ValueError("steps must be a non-empty exact audit tuple")
|
|
722
|
+
if tuple(value.step for value in self.steps) != tuple(
|
|
723
|
+
range(1, len(self.steps) + 1)
|
|
724
|
+
):
|
|
725
|
+
raise ValueError("audit steps must be contiguous and ordered")
|
|
726
|
+
for value in self.steps:
|
|
727
|
+
_validate_step_against_policy(
|
|
728
|
+
value,
|
|
729
|
+
resolution=self.score_resolution,
|
|
730
|
+
selection=self.tie_selection,
|
|
731
|
+
)
|
|
732
|
+
expected_count = sum(value.candidate_count for value in self.steps)
|
|
733
|
+
if (
|
|
734
|
+
type(self.candidate_score_count) is not int
|
|
735
|
+
or self.candidate_score_count != expected_count
|
|
736
|
+
):
|
|
737
|
+
raise ValueError("candidate_score_count differs from the complete tables")
|
|
738
|
+
if type(self.passes) is not bool or self.passes is not all(
|
|
739
|
+
value.passes for value in self.steps
|
|
740
|
+
):
|
|
741
|
+
raise ValueError("audit passes differs from its step decisions")
|
|
742
|
+
|
|
743
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
744
|
+
self.__post_init__()
|
|
745
|
+
return {
|
|
746
|
+
"schema_version": 1,
|
|
747
|
+
"operational_request_sha256": self.operational_request_sha256,
|
|
748
|
+
"base_allocation_request_sha256": self.base_allocation_request_sha256,
|
|
749
|
+
"decision_receipt_sha256": self.decision_receipt_sha256,
|
|
750
|
+
"frame_receipt_sha256": self.frame_receipt_sha256,
|
|
751
|
+
"score_resolution": self.score_resolution.to_record(),
|
|
752
|
+
"tie_selection": self.tie_selection.to_record(),
|
|
753
|
+
"steps": [value.to_record() for value in self.steps],
|
|
754
|
+
"candidate_score_count": self.candidate_score_count,
|
|
755
|
+
"passes": self.passes,
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
@property
|
|
759
|
+
def receipt_sha256(self) -> str:
|
|
760
|
+
return _hash(_AUDIT_DOMAIN, self._unsigned_record())
|
|
761
|
+
|
|
762
|
+
def to_record(self) -> dict[str, object]:
|
|
763
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
764
|
+
|
|
765
|
+
def __eq__(self, other: object) -> bool:
|
|
766
|
+
return (
|
|
767
|
+
type(other) is OperationalFrameActionAllocationAudit
|
|
768
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
__hash__ = None
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
775
|
+
class OperationalFrameActionAllocationResult:
|
|
776
|
+
decision: FrameActionPortfolioDecision
|
|
777
|
+
audit: OperationalFrameActionAllocationAudit
|
|
778
|
+
|
|
779
|
+
def __post_init__(self) -> None:
|
|
780
|
+
if type(self.decision) is not FrameActionPortfolioDecision:
|
|
781
|
+
raise TypeError("decision must be an exact frame portfolio decision")
|
|
782
|
+
self.decision.__post_init__()
|
|
783
|
+
if type(self.audit) is not OperationalFrameActionAllocationAudit:
|
|
784
|
+
raise TypeError("audit must be an exact operational allocation audit")
|
|
785
|
+
self.audit.__post_init__()
|
|
786
|
+
if self.audit.decision_receipt_sha256 != self.decision.receipt_sha256:
|
|
787
|
+
raise ValueError("audit is bound to another decision")
|
|
788
|
+
if self.audit.base_allocation_request_sha256 != (
|
|
789
|
+
self.decision.allocation_request_sha256
|
|
790
|
+
):
|
|
791
|
+
raise ValueError("audit and decision name different base requests")
|
|
792
|
+
if self.audit.frame_receipt_sha256 != self.decision.frame_receipt_sha256:
|
|
793
|
+
raise ValueError("audit and decision name different frames")
|
|
794
|
+
if self.audit.candidate_score_count != self.decision.candidate_evaluations:
|
|
795
|
+
raise ValueError("audit and decision candidate counts differ")
|
|
796
|
+
|
|
797
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
798
|
+
self.__post_init__()
|
|
799
|
+
return {
|
|
800
|
+
"schema_version": 1,
|
|
801
|
+
"decision_receipt_sha256": self.decision.receipt_sha256,
|
|
802
|
+
"audit_receipt_sha256": self.audit.receipt_sha256,
|
|
803
|
+
"authorized": self.audit.passes,
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
@property
|
|
807
|
+
def receipt_sha256(self) -> str:
|
|
808
|
+
return _hash(_RESULT_DOMAIN, self._unsigned_record())
|
|
809
|
+
|
|
810
|
+
def to_record(self) -> dict[str, object]:
|
|
811
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
812
|
+
|
|
813
|
+
def __eq__(self, other: object) -> bool:
|
|
814
|
+
return (
|
|
815
|
+
type(other) is OperationalFrameActionAllocationResult
|
|
816
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
__hash__ = None
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
def _recompute_candidate_score(
|
|
823
|
+
request: OperationalFrameActionAllocationRequest,
|
|
824
|
+
members: tuple[ResolvedActionForecast, ...],
|
|
825
|
+
*,
|
|
826
|
+
attainable_diversity: int,
|
|
827
|
+
) -> PortfolioAllocationScore:
|
|
828
|
+
"""Rerun the authenticated benchmark utility for validator independence."""
|
|
829
|
+
|
|
830
|
+
base = request.allocation
|
|
831
|
+
canonical_members = tuple(
|
|
832
|
+
sorted(
|
|
833
|
+
members,
|
|
834
|
+
key=lambda value: (value.option_identity_sha256, value.option_id),
|
|
835
|
+
)
|
|
836
|
+
)
|
|
837
|
+
utilities: dict[ForecastQuantile, float] = {}
|
|
838
|
+
for quantile in (ForecastQuantile.P10, ForecastQuantile.P50, ForecastQuantile.P90):
|
|
839
|
+
value = base.utility.utility(
|
|
840
|
+
ForecastPortfolioUtilityInput(
|
|
841
|
+
optimization_semantics=base.frame.request.optimization_semantics,
|
|
842
|
+
parent_metric_values=base.frame.request.parent_metric_values,
|
|
843
|
+
metric_scales=base.frame.request.metric_scales,
|
|
844
|
+
members=canonical_members,
|
|
845
|
+
quantile=quantile,
|
|
846
|
+
)
|
|
847
|
+
)
|
|
848
|
+
if type(value) is not float or not math.isfinite(value):
|
|
849
|
+
raise TypeError("bound portfolio utility must return a finite float")
|
|
850
|
+
utilities[quantile] = value
|
|
851
|
+
p10 = utilities[ForecastQuantile.P10]
|
|
852
|
+
p50 = utilities[ForecastQuantile.P50]
|
|
853
|
+
p90 = utilities[ForecastQuantile.P90]
|
|
854
|
+
downside = min(p10, p90)
|
|
855
|
+
risk_penalty = request.risk_aversion * max(0.0, p50 - downside)
|
|
856
|
+
diversity_reward = request.diversity_weight * (
|
|
857
|
+
len({value.family for value in canonical_members}) / attainable_diversity
|
|
858
|
+
)
|
|
859
|
+
primary = p50 - risk_penalty
|
|
860
|
+
total = primary + diversity_reward
|
|
861
|
+
if not all(
|
|
862
|
+
math.isfinite(value)
|
|
863
|
+
for value in (risk_penalty, diversity_reward, primary, total)
|
|
864
|
+
):
|
|
865
|
+
raise ValueError("recomputed allocator-v3 score became non-finite")
|
|
866
|
+
return PortfolioAllocationScore(
|
|
867
|
+
p10_utility=p10,
|
|
868
|
+
p50_utility=p50,
|
|
869
|
+
p90_utility=p90,
|
|
870
|
+
downside_utility=downside,
|
|
871
|
+
risk_penalty=risk_penalty,
|
|
872
|
+
diversity_reward=diversity_reward,
|
|
873
|
+
total_utility=total,
|
|
874
|
+
)
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
def validate_operational_frame_action_allocation_result(
|
|
878
|
+
request: OperationalFrameActionAllocationRequest,
|
|
879
|
+
result: OperationalFrameActionAllocationResult,
|
|
880
|
+
) -> None:
|
|
881
|
+
"""Validate every binding and rerun utility over all authenticated rows."""
|
|
882
|
+
|
|
883
|
+
if type(request) is not OperationalFrameActionAllocationRequest:
|
|
884
|
+
raise TypeError("request must be an exact operational frame request")
|
|
885
|
+
request.__post_init__()
|
|
886
|
+
if type(result) is not OperationalFrameActionAllocationResult:
|
|
887
|
+
raise TypeError("result must be an exact operational frame result")
|
|
888
|
+
result.__post_init__()
|
|
889
|
+
validate_frame_action_portfolio_decision(request.allocation, result.decision)
|
|
890
|
+
decision = result.decision
|
|
891
|
+
audit = result.audit
|
|
892
|
+
if (
|
|
893
|
+
decision.allocator_policy_id != GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_ID
|
|
894
|
+
or decision.allocator_policy_version
|
|
895
|
+
!= GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_VERSION
|
|
896
|
+
or decision.allocator_definition_sha256
|
|
897
|
+
!= GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_DEFINITION_SHA256
|
|
898
|
+
or decision.allocator_configuration_sha256
|
|
899
|
+
!= request.allocator_configuration_sha256
|
|
900
|
+
):
|
|
901
|
+
raise ValueError("decision names another allocator-v3 policy/configuration")
|
|
902
|
+
if (
|
|
903
|
+
audit.operational_request_sha256 != request.request_sha256
|
|
904
|
+
or audit.base_allocation_request_sha256 != request.allocation.request_sha256
|
|
905
|
+
or audit.frame_receipt_sha256 != request.allocation.frame.receipt_sha256
|
|
906
|
+
or audit.score_resolution != request.score_resolution
|
|
907
|
+
or audit.tie_selection != request.tie_selection
|
|
908
|
+
):
|
|
909
|
+
raise ValueError("operational audit is bound to another request")
|
|
910
|
+
expected_counts = tuple(
|
|
911
|
+
len(request.allocation.eligible_option_ids) - offset
|
|
912
|
+
for offset in range(request.allocation.portfolio_size)
|
|
913
|
+
)
|
|
914
|
+
if tuple(value.candidate_count for value in audit.steps) != expected_counts:
|
|
915
|
+
raise ValueError("v3 audit does not cover every greedy extension")
|
|
916
|
+
|
|
917
|
+
identity_by_label = {
|
|
918
|
+
f"row_{index:08d}": forecast.option_identity_sha256
|
|
919
|
+
for index, forecast in zip(
|
|
920
|
+
request.allocation.frame.global_row_indices,
|
|
921
|
+
request.allocation.frame.forecasts,
|
|
922
|
+
strict=True,
|
|
923
|
+
)
|
|
924
|
+
}
|
|
925
|
+
forecast_by_identity = {
|
|
926
|
+
value.option_identity_sha256: value
|
|
927
|
+
for value in request.allocation.frame.forecasts
|
|
928
|
+
if value.option_id in request.allocation.eligible_option_ids
|
|
929
|
+
}
|
|
930
|
+
remaining_identities = set(forecast_by_identity)
|
|
931
|
+
selected_forecasts = []
|
|
932
|
+
attainable_diversity = min(
|
|
933
|
+
request.allocation.portfolio_size,
|
|
934
|
+
len({value.family for value in forecast_by_identity.values()}),
|
|
935
|
+
)
|
|
936
|
+
previous_total = 0.0
|
|
937
|
+
for member, step in zip(decision.members, audit.steps, strict=True):
|
|
938
|
+
observed_identities = {
|
|
939
|
+
value.option_identity_sha256 for value in step.candidates
|
|
940
|
+
}
|
|
941
|
+
if observed_identities != remaining_identities:
|
|
942
|
+
raise ValueError("candidate table differs from the remaining eligible set")
|
|
943
|
+
for candidate in step.candidates:
|
|
944
|
+
if identity_by_label.get(candidate.candidate_label) != (
|
|
945
|
+
candidate.option_identity_sha256
|
|
946
|
+
):
|
|
947
|
+
raise ValueError("candidate label differs from its frame identity")
|
|
948
|
+
forecast = forecast_by_identity[candidate.option_identity_sha256]
|
|
949
|
+
expected_score = _recompute_candidate_score(
|
|
950
|
+
request,
|
|
951
|
+
tuple((*selected_forecasts, forecast)),
|
|
952
|
+
attainable_diversity=attainable_diversity,
|
|
953
|
+
)
|
|
954
|
+
if candidate.score != expected_score:
|
|
955
|
+
raise ValueError(
|
|
956
|
+
"candidate score differs from recomputed benchmark utility"
|
|
957
|
+
)
|
|
958
|
+
if candidate.marginal_total_utility != (
|
|
959
|
+
expected_score.total_utility - previous_total
|
|
960
|
+
):
|
|
961
|
+
raise ValueError("candidate marginal differs from its complete score")
|
|
962
|
+
selected = next(
|
|
963
|
+
value
|
|
964
|
+
for value in step.candidates
|
|
965
|
+
if value.candidate_label == step.selected_candidate_label
|
|
966
|
+
)
|
|
967
|
+
if (
|
|
968
|
+
identity_by_label[step.selected_candidate_label]
|
|
969
|
+
!= member.option_identity_sha256
|
|
970
|
+
or selected.option_identity_sha256 != member.option_identity_sha256
|
|
971
|
+
or selected.score != member.greedy_step_score
|
|
972
|
+
or selected.marginal_total_utility != member.marginal_total_utility
|
|
973
|
+
):
|
|
974
|
+
raise ValueError("decision member differs from its audited v3 winner")
|
|
975
|
+
selected_forecasts.append(forecast_by_identity[member.option_identity_sha256])
|
|
976
|
+
remaining_identities.remove(member.option_identity_sha256)
|
|
977
|
+
previous_total = selected.score.total_utility
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
__all__ = [
|
|
981
|
+
"AllocationScoreResolutionBinding",
|
|
982
|
+
"AllocationV3Candidate",
|
|
983
|
+
"AllocationV3SeedSamplingLaw",
|
|
984
|
+
"AllocationV3SelectionBinding",
|
|
985
|
+
"AllocationV3StepAudit",
|
|
986
|
+
"AllocationV3TieMode",
|
|
987
|
+
"GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_DEFINITION_SHA256",
|
|
988
|
+
"GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_ID",
|
|
989
|
+
"GREEDY_RISK_DIVERSITY_V3_ALLOCATOR_VERSION",
|
|
990
|
+
"OperationalFrameActionAllocationAudit",
|
|
991
|
+
"OperationalFrameActionAllocationRequest",
|
|
992
|
+
"OperationalFrameActionAllocationResult",
|
|
993
|
+
"allocation_v3_failure_codes",
|
|
994
|
+
"validate_operational_frame_action_allocation_result",
|
|
995
|
+
]
|