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,906 @@
|
|
|
1
|
+
"""Outcome-blind exposure floors over heterogeneous proposal sources.
|
|
2
|
+
|
|
3
|
+
The application core must be able to conserve genuinely different search
|
|
4
|
+
processes without learning what a "model", "provider", or workload is. This
|
|
5
|
+
module therefore exposes two small inverted APIs:
|
|
6
|
+
|
|
7
|
+
* a projection assigns every sealed materialized action to an opaque source
|
|
8
|
+
group; and
|
|
9
|
+
* an ordinary pre-evaluation score authority ranks candidates within groups.
|
|
10
|
+
|
|
11
|
+
The allocation decorator first obtains one complete K-slate from an injected
|
|
12
|
+
base policy. It then performs the minimum deterministic replacements needed
|
|
13
|
+
to satisfy declared group floors, choosing replacements and victims by the
|
|
14
|
+
same injected score. No current candidate outcome exists at this boundary.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
import hashlib
|
|
21
|
+
import json
|
|
22
|
+
import re
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from typing import ClassVar, Protocol, runtime_checkable
|
|
25
|
+
|
|
26
|
+
from agent_evolve.application.materialized_action_broker import (
|
|
27
|
+
MaterializedActionAllocationRequirement,
|
|
28
|
+
MaterializedActionDescriptor,
|
|
29
|
+
)
|
|
30
|
+
from agent_evolve.application.prequential_score_portfolio import (
|
|
31
|
+
MaterializedActionScoreBatch,
|
|
32
|
+
MaterializedActionScorePort,
|
|
33
|
+
)
|
|
34
|
+
from agent_evolve.application.residual_portfolio_evolution import (
|
|
35
|
+
MaterializedActionAllocationPolicyPort,
|
|
36
|
+
MaterializedActionProposalBatch,
|
|
37
|
+
ResidualPortfolioDecisionRequest,
|
|
38
|
+
)
|
|
39
|
+
from agent_evolve.domain.patch import require_sha256
|
|
40
|
+
from agent_evolve.domain.typed_json import (
|
|
41
|
+
FrozenJsonObject,
|
|
42
|
+
freeze_json,
|
|
43
|
+
thaw_json,
|
|
44
|
+
typed_json_sha256,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
SOURCE_EXPOSURE_ALLOCATION_POLICY_ID = (
|
|
48
|
+
"minimum_source_exposure_allocator"
|
|
49
|
+
)
|
|
50
|
+
SOURCE_EXPOSURE_ALLOCATION_POLICY_VERSION = 1
|
|
51
|
+
MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION = 1
|
|
52
|
+
EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_ID = (
|
|
53
|
+
"explicit_expert_source_group_projection"
|
|
54
|
+
)
|
|
55
|
+
EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION = 1
|
|
56
|
+
|
|
57
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
58
|
+
_ASSIGNMENT_BATCH_DOMAIN = (
|
|
59
|
+
b"agent-evolve:materialized-source-group-assignment-batch:v1\x00"
|
|
60
|
+
)
|
|
61
|
+
_PROJECTION_DEFINITION_DOMAIN = (
|
|
62
|
+
b"agent-evolve:explicit-expert-source-group-projection:v1\x00"
|
|
63
|
+
)
|
|
64
|
+
_POLICY_DEFINITION_DOMAIN = (
|
|
65
|
+
b"agent-evolve:minimum-source-exposure-allocation:v1\x00"
|
|
66
|
+
)
|
|
67
|
+
_SLATE_FEASIBILITY_DEFINITION_DOMAIN = (
|
|
68
|
+
b"agent-evolve:minimum-expert-source-exposure-slate-feasibility:v1\x00"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _canonical_json(value: object) -> bytes:
|
|
73
|
+
return json.dumps(
|
|
74
|
+
value,
|
|
75
|
+
allow_nan=False,
|
|
76
|
+
ensure_ascii=True,
|
|
77
|
+
separators=(",", ":"),
|
|
78
|
+
sort_keys=True,
|
|
79
|
+
).encode("ascii", errors="strict")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
83
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _require_token(value: str, *, name: str) -> None:
|
|
87
|
+
if type(value) is not str or _TOKEN.fullmatch(value) is None:
|
|
88
|
+
raise ValueError(f"{name} must use the closed token grammar")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _policy_identity(
|
|
92
|
+
value: MaterializedActionAllocationPolicyPort,
|
|
93
|
+
) -> tuple[str, int, str]:
|
|
94
|
+
if not isinstance(value, MaterializedActionAllocationPolicyPort):
|
|
95
|
+
raise TypeError("base policy must implement its application port")
|
|
96
|
+
identity = (
|
|
97
|
+
getattr(value, "policy_id", None),
|
|
98
|
+
getattr(value, "policy_version", None),
|
|
99
|
+
getattr(value, "definition_sha256", None),
|
|
100
|
+
)
|
|
101
|
+
_require_token(identity[0], name="base policy_id")
|
|
102
|
+
if type(identity[1]) is not int or identity[1] <= 0:
|
|
103
|
+
raise ValueError("base policy_version must be positive")
|
|
104
|
+
require_sha256(identity[2], "base policy definition_sha256")
|
|
105
|
+
return identity # type: ignore[return-value]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _scorer_identity(
|
|
109
|
+
value: MaterializedActionScorePort,
|
|
110
|
+
) -> tuple[str, int, str]:
|
|
111
|
+
if not isinstance(value, MaterializedActionScorePort):
|
|
112
|
+
raise TypeError("priority scorer must implement its application port")
|
|
113
|
+
identity = (
|
|
114
|
+
getattr(value, "scorer_id", None),
|
|
115
|
+
getattr(value, "scorer_version", None),
|
|
116
|
+
getattr(value, "definition_sha256", None),
|
|
117
|
+
)
|
|
118
|
+
_require_token(identity[0], name="priority scorer_id")
|
|
119
|
+
if type(identity[1]) is not int or identity[1] <= 0:
|
|
120
|
+
raise ValueError("priority scorer_version must be positive")
|
|
121
|
+
require_sha256(identity[2], "priority scorer definition_sha256")
|
|
122
|
+
return identity # type: ignore[return-value]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True, slots=True)
|
|
126
|
+
class MaterializedActionSourceGroup:
|
|
127
|
+
action_sha256: str
|
|
128
|
+
group_id: str
|
|
129
|
+
|
|
130
|
+
def __post_init__(self) -> None:
|
|
131
|
+
require_sha256(self.action_sha256, "action_sha256")
|
|
132
|
+
_require_token(self.group_id, name="group_id")
|
|
133
|
+
|
|
134
|
+
def to_record(self) -> dict[str, object]:
|
|
135
|
+
self.__post_init__()
|
|
136
|
+
return {
|
|
137
|
+
"action_sha256": self.action_sha256,
|
|
138
|
+
"group_id": self.group_id,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass(frozen=True, slots=True)
|
|
143
|
+
class MaterializedActionSourceGroupBatch:
|
|
144
|
+
projection_id: str
|
|
145
|
+
projection_version: int
|
|
146
|
+
projection_definition_sha256: str
|
|
147
|
+
residual_request_sha256: str
|
|
148
|
+
proposal_sha256s: tuple[str, ...]
|
|
149
|
+
assignments: tuple[MaterializedActionSourceGroup, ...]
|
|
150
|
+
candidate_outcomes_observed: bool
|
|
151
|
+
evidence: FrozenJsonObject
|
|
152
|
+
batch_sha256: str = field(init=False)
|
|
153
|
+
|
|
154
|
+
def __post_init__(self) -> None:
|
|
155
|
+
_require_token(self.projection_id, name="projection_id")
|
|
156
|
+
if type(self.projection_version) is not int or self.projection_version <= 0:
|
|
157
|
+
raise ValueError("projection_version must be positive")
|
|
158
|
+
require_sha256(
|
|
159
|
+
self.projection_definition_sha256,
|
|
160
|
+
"projection_definition_sha256",
|
|
161
|
+
)
|
|
162
|
+
require_sha256(
|
|
163
|
+
self.residual_request_sha256,
|
|
164
|
+
"residual_request_sha256",
|
|
165
|
+
)
|
|
166
|
+
if (
|
|
167
|
+
type(self.proposal_sha256s) is not tuple
|
|
168
|
+
or not self.proposal_sha256s
|
|
169
|
+
or self.proposal_sha256s
|
|
170
|
+
!= tuple(sorted(set(self.proposal_sha256s)))
|
|
171
|
+
):
|
|
172
|
+
raise ValueError("proposal_sha256s must be non-empty and canonical")
|
|
173
|
+
for value in self.proposal_sha256s:
|
|
174
|
+
require_sha256(value, "proposal_sha256")
|
|
175
|
+
if type(self.assignments) is not tuple or not self.assignments:
|
|
176
|
+
raise ValueError("assignments must be a non-empty exact tuple")
|
|
177
|
+
for value in self.assignments:
|
|
178
|
+
if type(value) is not MaterializedActionSourceGroup:
|
|
179
|
+
raise TypeError("assignments must contain exact values")
|
|
180
|
+
value.__post_init__()
|
|
181
|
+
action_ids = tuple(value.action_sha256 for value in self.assignments)
|
|
182
|
+
if action_ids != tuple(sorted(set(action_ids))):
|
|
183
|
+
raise ValueError("assignments must be action-canonical")
|
|
184
|
+
if type(self.candidate_outcomes_observed) is not bool:
|
|
185
|
+
raise TypeError("candidate_outcomes_observed must be exact")
|
|
186
|
+
if self.candidate_outcomes_observed:
|
|
187
|
+
raise ValueError("source groups cannot observe current outcomes")
|
|
188
|
+
if (
|
|
189
|
+
type(self.evidence) is not FrozenJsonObject
|
|
190
|
+
or freeze_json(self.evidence) is not self.evidence
|
|
191
|
+
):
|
|
192
|
+
raise TypeError("source-group evidence must be frozen")
|
|
193
|
+
object.__setattr__(
|
|
194
|
+
self,
|
|
195
|
+
"batch_sha256",
|
|
196
|
+
_hash(
|
|
197
|
+
_ASSIGNMENT_BATCH_DOMAIN,
|
|
198
|
+
{
|
|
199
|
+
"schema_version": 1,
|
|
200
|
+
"projection": {
|
|
201
|
+
"projection_id": self.projection_id,
|
|
202
|
+
"projection_version": self.projection_version,
|
|
203
|
+
"definition_sha256": (
|
|
204
|
+
self.projection_definition_sha256
|
|
205
|
+
),
|
|
206
|
+
},
|
|
207
|
+
"residual_request_sha256": (
|
|
208
|
+
self.residual_request_sha256
|
|
209
|
+
),
|
|
210
|
+
"proposal_sha256s": list(self.proposal_sha256s),
|
|
211
|
+
"assignments": [
|
|
212
|
+
value.to_record() for value in self.assignments
|
|
213
|
+
],
|
|
214
|
+
"candidate_outcomes_observed": False,
|
|
215
|
+
"evidence_sha256": typed_json_sha256(self.evidence),
|
|
216
|
+
},
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def to_record(self, *, include_evidence: bool = False) -> dict[str, object]:
|
|
221
|
+
self.__post_init__()
|
|
222
|
+
record = {
|
|
223
|
+
"schema_version": 1,
|
|
224
|
+
"projection": {
|
|
225
|
+
"projection_id": self.projection_id,
|
|
226
|
+
"projection_version": self.projection_version,
|
|
227
|
+
"definition_sha256": self.projection_definition_sha256,
|
|
228
|
+
},
|
|
229
|
+
"residual_request_sha256": self.residual_request_sha256,
|
|
230
|
+
"proposal_sha256s": list(self.proposal_sha256s),
|
|
231
|
+
"assignments": [
|
|
232
|
+
value.to_record() for value in self.assignments
|
|
233
|
+
],
|
|
234
|
+
"candidate_outcomes_observed": False,
|
|
235
|
+
"evidence_sha256": typed_json_sha256(self.evidence),
|
|
236
|
+
"batch_sha256": self.batch_sha256,
|
|
237
|
+
}
|
|
238
|
+
if include_evidence:
|
|
239
|
+
record["evidence"] = thaw_json(self.evidence)
|
|
240
|
+
return record
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@runtime_checkable
|
|
244
|
+
class MaterializedActionSourceGroupProjectionPort(Protocol):
|
|
245
|
+
projection_id: str
|
|
246
|
+
projection_version: int
|
|
247
|
+
definition_sha256: str
|
|
248
|
+
|
|
249
|
+
async def project(
|
|
250
|
+
self,
|
|
251
|
+
request: ResidualPortfolioDecisionRequest,
|
|
252
|
+
proposals: tuple[MaterializedActionProposalBatch, ...],
|
|
253
|
+
) -> MaterializedActionSourceGroupBatch: ...
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _projection_identity(
|
|
257
|
+
value: MaterializedActionSourceGroupProjectionPort,
|
|
258
|
+
) -> tuple[str, int, str]:
|
|
259
|
+
if not isinstance(value, MaterializedActionSourceGroupProjectionPort):
|
|
260
|
+
raise TypeError("source projection must implement its application port")
|
|
261
|
+
identity = (
|
|
262
|
+
getattr(value, "projection_id", None),
|
|
263
|
+
getattr(value, "projection_version", None),
|
|
264
|
+
getattr(value, "definition_sha256", None),
|
|
265
|
+
)
|
|
266
|
+
_require_token(identity[0], name="source projection_id")
|
|
267
|
+
if type(identity[1]) is not int or identity[1] <= 0:
|
|
268
|
+
raise ValueError("source projection_version must be positive")
|
|
269
|
+
require_sha256(identity[2], "source projection definition_sha256")
|
|
270
|
+
return identity # type: ignore[return-value]
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@dataclass(frozen=True, slots=True)
|
|
274
|
+
class ExplicitExpertSourceGroupProjection:
|
|
275
|
+
"""Map opaque expert identities to opaque source groups."""
|
|
276
|
+
|
|
277
|
+
expert_group_bindings: tuple[tuple[str, str], ...]
|
|
278
|
+
projection_id: str = EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_ID
|
|
279
|
+
projection_version: int = (
|
|
280
|
+
EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION
|
|
281
|
+
)
|
|
282
|
+
definition_sha256: str = field(init=False)
|
|
283
|
+
|
|
284
|
+
def __post_init__(self) -> None:
|
|
285
|
+
if (
|
|
286
|
+
type(self.expert_group_bindings) is not tuple
|
|
287
|
+
or not self.expert_group_bindings
|
|
288
|
+
or self.expert_group_bindings
|
|
289
|
+
!= tuple(sorted(self.expert_group_bindings))
|
|
290
|
+
):
|
|
291
|
+
raise ValueError(
|
|
292
|
+
"expert_group_bindings must be non-empty and canonical"
|
|
293
|
+
)
|
|
294
|
+
expert_ids: list[str] = []
|
|
295
|
+
for expert_id, group_id in self.expert_group_bindings:
|
|
296
|
+
_require_token(expert_id, name="expert_id")
|
|
297
|
+
_require_token(group_id, name="group_id")
|
|
298
|
+
expert_ids.append(expert_id)
|
|
299
|
+
if len(expert_ids) != len(set(expert_ids)):
|
|
300
|
+
raise ValueError("expert_group_bindings repeat an expert")
|
|
301
|
+
_require_token(self.projection_id, name="projection_id")
|
|
302
|
+
if (
|
|
303
|
+
self.projection_version
|
|
304
|
+
!= EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION
|
|
305
|
+
):
|
|
306
|
+
raise ValueError("projection_version is immutable")
|
|
307
|
+
object.__setattr__(
|
|
308
|
+
self,
|
|
309
|
+
"definition_sha256",
|
|
310
|
+
_hash(
|
|
311
|
+
_PROJECTION_DEFINITION_DOMAIN,
|
|
312
|
+
{
|
|
313
|
+
"schema_version": 1,
|
|
314
|
+
"projection_id": self.projection_id,
|
|
315
|
+
"projection_version": self.projection_version,
|
|
316
|
+
"expert_group_bindings": [
|
|
317
|
+
{
|
|
318
|
+
"expert_id": expert_id,
|
|
319
|
+
"group_id": group_id,
|
|
320
|
+
}
|
|
321
|
+
for expert_id, group_id
|
|
322
|
+
in self.expert_group_bindings
|
|
323
|
+
],
|
|
324
|
+
"candidate_outcomes_observed": False,
|
|
325
|
+
"workload_model_provider_branches": False,
|
|
326
|
+
},
|
|
327
|
+
),
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
async def project(
|
|
331
|
+
self,
|
|
332
|
+
request: ResidualPortfolioDecisionRequest,
|
|
333
|
+
proposals: tuple[MaterializedActionProposalBatch, ...],
|
|
334
|
+
) -> MaterializedActionSourceGroupBatch:
|
|
335
|
+
self.__post_init__()
|
|
336
|
+
if type(request) is not ResidualPortfolioDecisionRequest:
|
|
337
|
+
raise TypeError("request must be exact")
|
|
338
|
+
request.__post_init__()
|
|
339
|
+
if type(proposals) is not tuple or not proposals:
|
|
340
|
+
raise ValueError("proposals must be a non-empty exact tuple")
|
|
341
|
+
group_by_expert = dict(self.expert_group_bindings)
|
|
342
|
+
assignments: list[MaterializedActionSourceGroup] = []
|
|
343
|
+
proposal_sha256s: list[str] = []
|
|
344
|
+
for proposal in proposals:
|
|
345
|
+
if type(proposal) is not MaterializedActionProposalBatch:
|
|
346
|
+
raise TypeError("proposals must contain exact batches")
|
|
347
|
+
proposal.__post_init__()
|
|
348
|
+
proposal.require_request(request)
|
|
349
|
+
group_id = group_by_expert.get(proposal.expert_id)
|
|
350
|
+
if group_id is None:
|
|
351
|
+
raise ValueError("proposal expert has no source-group binding")
|
|
352
|
+
proposal_sha256s.append(proposal.proposal_sha256)
|
|
353
|
+
assignments.extend(
|
|
354
|
+
MaterializedActionSourceGroup(
|
|
355
|
+
action_sha256=action.action_sha256,
|
|
356
|
+
group_id=group_id,
|
|
357
|
+
)
|
|
358
|
+
for action in proposal.actions
|
|
359
|
+
)
|
|
360
|
+
if set(group_by_expert) != {
|
|
361
|
+
value.expert_id for value in proposals
|
|
362
|
+
}:
|
|
363
|
+
raise ValueError("source-group bindings name absent experts")
|
|
364
|
+
return MaterializedActionSourceGroupBatch(
|
|
365
|
+
projection_id=self.projection_id,
|
|
366
|
+
projection_version=self.projection_version,
|
|
367
|
+
projection_definition_sha256=self.definition_sha256,
|
|
368
|
+
residual_request_sha256=request.request_sha256,
|
|
369
|
+
proposal_sha256s=tuple(sorted(proposal_sha256s)),
|
|
370
|
+
assignments=tuple(
|
|
371
|
+
sorted(assignments, key=lambda value: value.action_sha256)
|
|
372
|
+
),
|
|
373
|
+
candidate_outcomes_observed=False,
|
|
374
|
+
evidence=freeze_json(
|
|
375
|
+
{
|
|
376
|
+
"expert_count": len(group_by_expert),
|
|
377
|
+
"group_ids": sorted(set(group_by_expert.values())),
|
|
378
|
+
"mapping_is_explicit": True,
|
|
379
|
+
"candidate_outcomes_observed": False,
|
|
380
|
+
"workload_model_provider_branches": False,
|
|
381
|
+
}
|
|
382
|
+
),
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
@dataclass(frozen=True, slots=True)
|
|
387
|
+
class MinimumExpertSourceExposureSlateFeasibility:
|
|
388
|
+
"""Require phenotype uniqueness and opaque expert-group exposure floors."""
|
|
389
|
+
|
|
390
|
+
expert_group_bindings: tuple[tuple[str, str], ...]
|
|
391
|
+
minimum_exposures: tuple[tuple[str, int], ...]
|
|
392
|
+
feasibility_version: int = (
|
|
393
|
+
MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION
|
|
394
|
+
)
|
|
395
|
+
definition_sha256: str = field(init=False)
|
|
396
|
+
|
|
397
|
+
def __post_init__(self) -> None:
|
|
398
|
+
projection = ExplicitExpertSourceGroupProjection(
|
|
399
|
+
expert_group_bindings=self.expert_group_bindings,
|
|
400
|
+
)
|
|
401
|
+
if (
|
|
402
|
+
type(self.minimum_exposures) is not tuple
|
|
403
|
+
or not self.minimum_exposures
|
|
404
|
+
or self.minimum_exposures
|
|
405
|
+
!= tuple(sorted(self.minimum_exposures))
|
|
406
|
+
):
|
|
407
|
+
raise ValueError(
|
|
408
|
+
"minimum_exposures must be non-empty and canonical"
|
|
409
|
+
)
|
|
410
|
+
group_ids: list[str] = []
|
|
411
|
+
for group_id, minimum in self.minimum_exposures:
|
|
412
|
+
_require_token(group_id, name="minimum exposure group_id")
|
|
413
|
+
if type(minimum) is not int or minimum <= 0:
|
|
414
|
+
raise ValueError("minimum exposures must be positive")
|
|
415
|
+
group_ids.append(group_id)
|
|
416
|
+
if len(group_ids) != len(set(group_ids)):
|
|
417
|
+
raise ValueError("minimum_exposures repeat a group")
|
|
418
|
+
available_groups = {
|
|
419
|
+
group_id for _expert_id, group_id
|
|
420
|
+
in self.expert_group_bindings
|
|
421
|
+
}
|
|
422
|
+
if not set(group_ids) <= available_groups:
|
|
423
|
+
raise ValueError("minimum exposure group has no expert binding")
|
|
424
|
+
if (
|
|
425
|
+
self.feasibility_version
|
|
426
|
+
!= MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION
|
|
427
|
+
):
|
|
428
|
+
raise ValueError("feasibility_version is immutable")
|
|
429
|
+
object.__setattr__(
|
|
430
|
+
self,
|
|
431
|
+
"definition_sha256",
|
|
432
|
+
_hash(
|
|
433
|
+
_SLATE_FEASIBILITY_DEFINITION_DOMAIN,
|
|
434
|
+
{
|
|
435
|
+
"schema_version": 1,
|
|
436
|
+
"feasibility_version": self.feasibility_version,
|
|
437
|
+
"source_projection_definition_sha256": (
|
|
438
|
+
projection.definition_sha256
|
|
439
|
+
),
|
|
440
|
+
"expert_group_bindings": [
|
|
441
|
+
{
|
|
442
|
+
"expert_id": expert_id,
|
|
443
|
+
"group_id": group_id,
|
|
444
|
+
}
|
|
445
|
+
for expert_id, group_id
|
|
446
|
+
in self.expert_group_bindings
|
|
447
|
+
],
|
|
448
|
+
"minimum_exposures": [
|
|
449
|
+
{"group_id": group_id, "minimum": minimum}
|
|
450
|
+
for group_id, minimum in self.minimum_exposures
|
|
451
|
+
],
|
|
452
|
+
"phenotype_unique": True,
|
|
453
|
+
"workload_model_provider_branches": False,
|
|
454
|
+
},
|
|
455
|
+
),
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
def permits(
|
|
459
|
+
self,
|
|
460
|
+
actions: tuple[MaterializedActionDescriptor, ...],
|
|
461
|
+
) -> bool:
|
|
462
|
+
self.__post_init__()
|
|
463
|
+
if type(actions) is not tuple or not actions:
|
|
464
|
+
return False
|
|
465
|
+
if any(
|
|
466
|
+
type(value) is not MaterializedActionDescriptor
|
|
467
|
+
for value in actions
|
|
468
|
+
):
|
|
469
|
+
raise TypeError("slate contains a foreign action")
|
|
470
|
+
for value in actions:
|
|
471
|
+
value.__post_init__()
|
|
472
|
+
phenotypes = tuple(
|
|
473
|
+
value.phenotype_identity_sha256 for value in actions
|
|
474
|
+
)
|
|
475
|
+
if len(phenotypes) != len(set(phenotypes)):
|
|
476
|
+
return False
|
|
477
|
+
group_by_expert = dict(self.expert_group_bindings)
|
|
478
|
+
groups = []
|
|
479
|
+
for value in actions:
|
|
480
|
+
group_id = group_by_expert.get(value.expert_id)
|
|
481
|
+
if group_id is None:
|
|
482
|
+
return False
|
|
483
|
+
groups.append(group_id)
|
|
484
|
+
return all(
|
|
485
|
+
groups.count(group_id) >= minimum
|
|
486
|
+
for group_id, minimum in self.minimum_exposures
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _selection_trace(
|
|
491
|
+
requirement: MaterializedActionAllocationRequirement,
|
|
492
|
+
) -> tuple[dict[str, object], ...]:
|
|
493
|
+
evidence = thaw_json(requirement.evidence)
|
|
494
|
+
if type(evidence) is not dict:
|
|
495
|
+
raise TypeError("base allocation evidence must be an object")
|
|
496
|
+
raw = evidence.get("selection_trace")
|
|
497
|
+
if type(raw) is not list:
|
|
498
|
+
raise ValueError("base allocation evidence lacks selection_trace")
|
|
499
|
+
rows: list[dict[str, object]] = []
|
|
500
|
+
seen: set[str] = set()
|
|
501
|
+
for fallback, value in enumerate(raw, start=1):
|
|
502
|
+
if type(value) is not dict:
|
|
503
|
+
raise TypeError("base selection rows must be objects")
|
|
504
|
+
row = dict(value)
|
|
505
|
+
action_sha256 = row.get("action_sha256")
|
|
506
|
+
if type(action_sha256) is not str:
|
|
507
|
+
raise TypeError("base selection row lacks action_sha256")
|
|
508
|
+
require_sha256(action_sha256, "base action_sha256")
|
|
509
|
+
if action_sha256 in seen:
|
|
510
|
+
raise ValueError("base selection trace repeats an action")
|
|
511
|
+
seen.add(action_sha256)
|
|
512
|
+
ordinal = row.get("ordinal", fallback)
|
|
513
|
+
if type(ordinal) is not int or ordinal <= 0:
|
|
514
|
+
raise ValueError("base selection ordinal must be positive")
|
|
515
|
+
row["ordinal"] = ordinal
|
|
516
|
+
rows.append(row)
|
|
517
|
+
if seen != set(requirement.required_action_sha256s):
|
|
518
|
+
raise ValueError("base selection trace differs from required actions")
|
|
519
|
+
return tuple(
|
|
520
|
+
sorted(
|
|
521
|
+
rows,
|
|
522
|
+
key=lambda value: (
|
|
523
|
+
int(value["ordinal"]),
|
|
524
|
+
str(value["action_sha256"]),
|
|
525
|
+
),
|
|
526
|
+
)
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
@dataclass(frozen=True, slots=True)
|
|
531
|
+
class MinimumSourceExposureAllocationPolicy:
|
|
532
|
+
"""Decorate a complete base slate with score-ranked source floors."""
|
|
533
|
+
|
|
534
|
+
base_policy: MaterializedActionAllocationPolicyPort = field(
|
|
535
|
+
repr=False,
|
|
536
|
+
compare=False,
|
|
537
|
+
)
|
|
538
|
+
priority_scorer: MaterializedActionScorePort = field(
|
|
539
|
+
repr=False,
|
|
540
|
+
compare=False,
|
|
541
|
+
)
|
|
542
|
+
source_projection: MaterializedActionSourceGroupProjectionPort = field(
|
|
543
|
+
repr=False,
|
|
544
|
+
compare=False,
|
|
545
|
+
)
|
|
546
|
+
minimum_exposures: tuple[tuple[str, int], ...]
|
|
547
|
+
policy_id: ClassVar[str] = SOURCE_EXPOSURE_ALLOCATION_POLICY_ID
|
|
548
|
+
policy_version: ClassVar[int] = (
|
|
549
|
+
SOURCE_EXPOSURE_ALLOCATION_POLICY_VERSION
|
|
550
|
+
)
|
|
551
|
+
definition_sha256: str = field(init=False)
|
|
552
|
+
|
|
553
|
+
def __post_init__(self) -> None:
|
|
554
|
+
base_identity = _policy_identity(self.base_policy)
|
|
555
|
+
scorer_identity = _scorer_identity(self.priority_scorer)
|
|
556
|
+
projection_identity = _projection_identity(
|
|
557
|
+
self.source_projection
|
|
558
|
+
)
|
|
559
|
+
if (
|
|
560
|
+
type(self.minimum_exposures) is not tuple
|
|
561
|
+
or not self.minimum_exposures
|
|
562
|
+
or self.minimum_exposures
|
|
563
|
+
!= tuple(sorted(self.minimum_exposures))
|
|
564
|
+
):
|
|
565
|
+
raise ValueError(
|
|
566
|
+
"minimum_exposures must be non-empty and canonical"
|
|
567
|
+
)
|
|
568
|
+
group_ids: list[str] = []
|
|
569
|
+
for group_id, minimum in self.minimum_exposures:
|
|
570
|
+
_require_token(group_id, name="minimum exposure group_id")
|
|
571
|
+
if type(minimum) is not int or minimum <= 0:
|
|
572
|
+
raise ValueError("minimum exposures must be positive")
|
|
573
|
+
group_ids.append(group_id)
|
|
574
|
+
if len(group_ids) != len(set(group_ids)):
|
|
575
|
+
raise ValueError("minimum_exposures repeat a group")
|
|
576
|
+
object.__setattr__(
|
|
577
|
+
self,
|
|
578
|
+
"definition_sha256",
|
|
579
|
+
_hash(
|
|
580
|
+
_POLICY_DEFINITION_DOMAIN,
|
|
581
|
+
{
|
|
582
|
+
"schema_version": 1,
|
|
583
|
+
"policy_id": self.policy_id,
|
|
584
|
+
"policy_version": self.policy_version,
|
|
585
|
+
"base_policy": {
|
|
586
|
+
"policy_id": base_identity[0],
|
|
587
|
+
"policy_version": base_identity[1],
|
|
588
|
+
"definition_sha256": base_identity[2],
|
|
589
|
+
},
|
|
590
|
+
"priority_scorer": {
|
|
591
|
+
"scorer_id": scorer_identity[0],
|
|
592
|
+
"scorer_version": scorer_identity[1],
|
|
593
|
+
"definition_sha256": scorer_identity[2],
|
|
594
|
+
},
|
|
595
|
+
"source_projection": {
|
|
596
|
+
"projection_id": projection_identity[0],
|
|
597
|
+
"projection_version": projection_identity[1],
|
|
598
|
+
"definition_sha256": projection_identity[2],
|
|
599
|
+
},
|
|
600
|
+
"minimum_exposures": [
|
|
601
|
+
{"group_id": group_id, "minimum": minimum}
|
|
602
|
+
for group_id, minimum in self.minimum_exposures
|
|
603
|
+
],
|
|
604
|
+
"repair": (
|
|
605
|
+
"minimum_score_ranked_replacements_of_lowest_score_"
|
|
606
|
+
"removable_members"
|
|
607
|
+
),
|
|
608
|
+
"candidate_outcomes_observed": False,
|
|
609
|
+
"workload_model_provider_branches": False,
|
|
610
|
+
},
|
|
611
|
+
),
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
async def require(
|
|
615
|
+
self,
|
|
616
|
+
request: ResidualPortfolioDecisionRequest,
|
|
617
|
+
proposals: tuple[MaterializedActionProposalBatch, ...],
|
|
618
|
+
) -> MaterializedActionAllocationRequirement:
|
|
619
|
+
self.__post_init__()
|
|
620
|
+
if type(request) is not ResidualPortfolioDecisionRequest:
|
|
621
|
+
raise TypeError("request must be exact")
|
|
622
|
+
request.__post_init__()
|
|
623
|
+
if type(proposals) is not tuple or not proposals:
|
|
624
|
+
raise ValueError("proposals must be a non-empty exact tuple")
|
|
625
|
+
actions: list[MaterializedActionDescriptor] = []
|
|
626
|
+
for proposal in proposals:
|
|
627
|
+
if type(proposal) is not MaterializedActionProposalBatch:
|
|
628
|
+
raise TypeError("proposals must contain exact batches")
|
|
629
|
+
proposal.__post_init__()
|
|
630
|
+
proposal.require_request(request)
|
|
631
|
+
actions.extend(proposal.actions)
|
|
632
|
+
action_by_sha256 = {
|
|
633
|
+
value.action_sha256: value for value in actions
|
|
634
|
+
}
|
|
635
|
+
if len(action_by_sha256) != len(actions):
|
|
636
|
+
raise ValueError("proposal market repeats an action")
|
|
637
|
+
proposal_sha256s = tuple(
|
|
638
|
+
sorted(value.proposal_sha256 for value in proposals)
|
|
639
|
+
)
|
|
640
|
+
base, score_batch, group_batch = await asyncio.gather(
|
|
641
|
+
self.base_policy.require(request, proposals),
|
|
642
|
+
self.priority_scorer.score(request, proposals),
|
|
643
|
+
self.source_projection.project(request, proposals),
|
|
644
|
+
)
|
|
645
|
+
if type(base) is not MaterializedActionAllocationRequirement:
|
|
646
|
+
raise TypeError("base policy returned a foreign requirement")
|
|
647
|
+
base.__post_init__()
|
|
648
|
+
if type(score_batch) is not MaterializedActionScoreBatch:
|
|
649
|
+
raise TypeError("priority scorer returned a foreign batch")
|
|
650
|
+
score_batch.__post_init__()
|
|
651
|
+
if type(group_batch) is not MaterializedActionSourceGroupBatch:
|
|
652
|
+
raise TypeError("source projection returned a foreign batch")
|
|
653
|
+
group_batch.__post_init__()
|
|
654
|
+
if (
|
|
655
|
+
(
|
|
656
|
+
base.residual_request_sha256,
|
|
657
|
+
base.proposal_sha256s,
|
|
658
|
+
)
|
|
659
|
+
!= (request.request_sha256, proposal_sha256s)
|
|
660
|
+
or (
|
|
661
|
+
score_batch.residual_request_sha256,
|
|
662
|
+
score_batch.proposal_sha256s,
|
|
663
|
+
)
|
|
664
|
+
!= (request.request_sha256, proposal_sha256s)
|
|
665
|
+
or (
|
|
666
|
+
group_batch.residual_request_sha256,
|
|
667
|
+
group_batch.proposal_sha256s,
|
|
668
|
+
)
|
|
669
|
+
!= (request.request_sha256, proposal_sha256s)
|
|
670
|
+
):
|
|
671
|
+
raise ValueError("allocation inputs saw different sealed markets")
|
|
672
|
+
action_ids = tuple(sorted(action_by_sha256))
|
|
673
|
+
if (
|
|
674
|
+
tuple(value.action_sha256 for value in score_batch.scores)
|
|
675
|
+
!= action_ids
|
|
676
|
+
or tuple(
|
|
677
|
+
value.action_sha256 for value in group_batch.assignments
|
|
678
|
+
)
|
|
679
|
+
!= action_ids
|
|
680
|
+
):
|
|
681
|
+
raise ValueError("score/group batches do not cover the market")
|
|
682
|
+
if len(base.required_action_sha256s) != request.evaluation_slots:
|
|
683
|
+
raise ValueError("source exposure requires a complete base K-slate")
|
|
684
|
+
if sum(value for _group, value in self.minimum_exposures) > (
|
|
685
|
+
request.evaluation_slots
|
|
686
|
+
):
|
|
687
|
+
raise ValueError("minimum source exposures exceed K")
|
|
688
|
+
|
|
689
|
+
score_by_action = {
|
|
690
|
+
value.action_sha256: value.value
|
|
691
|
+
for value in score_batch.scores
|
|
692
|
+
}
|
|
693
|
+
group_by_action = {
|
|
694
|
+
value.action_sha256: value.group_id
|
|
695
|
+
for value in group_batch.assignments
|
|
696
|
+
}
|
|
697
|
+
minima = dict(self.minimum_exposures)
|
|
698
|
+
if not set(minima).issubset(set(group_by_action.values())):
|
|
699
|
+
raise ValueError("a required source group is absent")
|
|
700
|
+
base_trace = _selection_trace(base)
|
|
701
|
+
selected = [
|
|
702
|
+
action_by_sha256[str(value["action_sha256"])]
|
|
703
|
+
for value in base_trace
|
|
704
|
+
]
|
|
705
|
+
if len(
|
|
706
|
+
{value.phenotype_identity_sha256 for value in selected}
|
|
707
|
+
) != len(selected):
|
|
708
|
+
raise ValueError("base K-slate repeats a phenotype")
|
|
709
|
+
original_rows = {
|
|
710
|
+
str(value["action_sha256"]): dict(value)
|
|
711
|
+
for value in base_trace
|
|
712
|
+
}
|
|
713
|
+
inserted: set[str] = set()
|
|
714
|
+
replacements: list[dict[str, object]] = []
|
|
715
|
+
|
|
716
|
+
def group_counts() -> dict[str, int]:
|
|
717
|
+
result: dict[str, int] = {}
|
|
718
|
+
for action in selected:
|
|
719
|
+
group_id = group_by_action[action.action_sha256]
|
|
720
|
+
result[group_id] = result.get(group_id, 0) + 1
|
|
721
|
+
return result
|
|
722
|
+
|
|
723
|
+
rankings = tuple(
|
|
724
|
+
sorted(
|
|
725
|
+
actions,
|
|
726
|
+
key=lambda value: (
|
|
727
|
+
-score_by_action[value.action_sha256],
|
|
728
|
+
value.native_rank,
|
|
729
|
+
value.expert_id,
|
|
730
|
+
value.action_sha256,
|
|
731
|
+
),
|
|
732
|
+
)
|
|
733
|
+
)
|
|
734
|
+
for group_id, minimum in self.minimum_exposures:
|
|
735
|
+
while group_counts().get(group_id, 0) < minimum:
|
|
736
|
+
counts = group_counts()
|
|
737
|
+
removable_indices = tuple(
|
|
738
|
+
index
|
|
739
|
+
for index, action in enumerate(selected)
|
|
740
|
+
if not action.reference_action
|
|
741
|
+
and counts[group_by_action[action.action_sha256]]
|
|
742
|
+
> minima.get(
|
|
743
|
+
group_by_action[action.action_sha256],
|
|
744
|
+
0,
|
|
745
|
+
)
|
|
746
|
+
)
|
|
747
|
+
if not removable_indices:
|
|
748
|
+
raise ValueError(
|
|
749
|
+
"source floors leave no removable base member"
|
|
750
|
+
)
|
|
751
|
+
replacement_pair: tuple[
|
|
752
|
+
MaterializedActionDescriptor,
|
|
753
|
+
int,
|
|
754
|
+
] | None = None
|
|
755
|
+
selected_ids = {
|
|
756
|
+
value.action_sha256 for value in selected
|
|
757
|
+
}
|
|
758
|
+
for candidate in rankings:
|
|
759
|
+
if (
|
|
760
|
+
candidate.action_sha256 in selected_ids
|
|
761
|
+
or group_by_action[candidate.action_sha256]
|
|
762
|
+
!= group_id
|
|
763
|
+
):
|
|
764
|
+
continue
|
|
765
|
+
eligible_victims = tuple(
|
|
766
|
+
index
|
|
767
|
+
for index in removable_indices
|
|
768
|
+
if all(
|
|
769
|
+
other_index == index
|
|
770
|
+
or other.phenotype_identity_sha256
|
|
771
|
+
!= candidate.phenotype_identity_sha256
|
|
772
|
+
for other_index, other in enumerate(selected)
|
|
773
|
+
)
|
|
774
|
+
)
|
|
775
|
+
if not eligible_victims:
|
|
776
|
+
continue
|
|
777
|
+
victim_index = min(
|
|
778
|
+
eligible_victims,
|
|
779
|
+
key=lambda index: (
|
|
780
|
+
score_by_action[
|
|
781
|
+
selected[index].action_sha256
|
|
782
|
+
],
|
|
783
|
+
-selected[index].native_rank,
|
|
784
|
+
selected[index].action_sha256,
|
|
785
|
+
),
|
|
786
|
+
)
|
|
787
|
+
replacement_pair = (candidate, victim_index)
|
|
788
|
+
break
|
|
789
|
+
if replacement_pair is None:
|
|
790
|
+
raise ValueError(
|
|
791
|
+
"source floor lacks a unique-phenotype replacement"
|
|
792
|
+
)
|
|
793
|
+
candidate, victim_index = replacement_pair
|
|
794
|
+
victim = selected[victim_index]
|
|
795
|
+
selected[victim_index] = candidate
|
|
796
|
+
inserted.add(candidate.action_sha256)
|
|
797
|
+
replacements.append(
|
|
798
|
+
{
|
|
799
|
+
"ordinal": len(replacements) + 1,
|
|
800
|
+
"required_group_id": group_id,
|
|
801
|
+
"inserted_action_sha256": (
|
|
802
|
+
candidate.action_sha256
|
|
803
|
+
),
|
|
804
|
+
"inserted_score_hex": score_by_action[
|
|
805
|
+
candidate.action_sha256
|
|
806
|
+
].hex(),
|
|
807
|
+
"removed_action_sha256": victim.action_sha256,
|
|
808
|
+
"removed_group_id": group_by_action[
|
|
809
|
+
victim.action_sha256
|
|
810
|
+
],
|
|
811
|
+
"removed_score_hex": score_by_action[
|
|
812
|
+
victim.action_sha256
|
|
813
|
+
].hex(),
|
|
814
|
+
"candidate_outcomes_observed": False,
|
|
815
|
+
}
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
final_counts = group_counts()
|
|
819
|
+
if any(
|
|
820
|
+
final_counts.get(group_id, 0) < minimum
|
|
821
|
+
for group_id, minimum in self.minimum_exposures
|
|
822
|
+
):
|
|
823
|
+
raise AssertionError("source exposure repair did not close")
|
|
824
|
+
if len(selected) != request.evaluation_slots or len(
|
|
825
|
+
{value.phenotype_identity_sha256 for value in selected}
|
|
826
|
+
) != len(selected):
|
|
827
|
+
raise AssertionError("source exposure repair corrupted K")
|
|
828
|
+
selection_trace: list[dict[str, object]] = []
|
|
829
|
+
for ordinal, action in enumerate(selected, start=1):
|
|
830
|
+
if action.action_sha256 in inserted:
|
|
831
|
+
row: dict[str, object] = {
|
|
832
|
+
"ordinal": ordinal,
|
|
833
|
+
"allocation_kind": "source_exposure_floor",
|
|
834
|
+
"score_lane": score_batch.scorer_id,
|
|
835
|
+
"action_sha256": action.action_sha256,
|
|
836
|
+
"score_hex": score_by_action[
|
|
837
|
+
action.action_sha256
|
|
838
|
+
].hex(),
|
|
839
|
+
"source_group_id": group_by_action[
|
|
840
|
+
action.action_sha256
|
|
841
|
+
],
|
|
842
|
+
"candidate_outcomes_observed": False,
|
|
843
|
+
}
|
|
844
|
+
else:
|
|
845
|
+
row = dict(original_rows[action.action_sha256])
|
|
846
|
+
row["ordinal"] = ordinal
|
|
847
|
+
row["source_group_id"] = group_by_action[
|
|
848
|
+
action.action_sha256
|
|
849
|
+
]
|
|
850
|
+
row["source_exposure_status"] = "base_retained"
|
|
851
|
+
selection_trace.append(row)
|
|
852
|
+
return MaterializedActionAllocationRequirement(
|
|
853
|
+
policy_id=self.policy_id,
|
|
854
|
+
policy_version=self.policy_version,
|
|
855
|
+
policy_definition_sha256=self.definition_sha256,
|
|
856
|
+
residual_request_sha256=request.request_sha256,
|
|
857
|
+
proposal_sha256s=proposal_sha256s,
|
|
858
|
+
required_action_sha256s=tuple(
|
|
859
|
+
sorted(value.action_sha256 for value in selected)
|
|
860
|
+
),
|
|
861
|
+
candidate_outcomes_observed=False,
|
|
862
|
+
evidence=freeze_json(
|
|
863
|
+
{
|
|
864
|
+
"base_requirement_sha256": base.requirement_sha256,
|
|
865
|
+
"priority_score_batch_sha256": (
|
|
866
|
+
score_batch.batch_sha256
|
|
867
|
+
),
|
|
868
|
+
"source_group_batch_sha256": group_batch.batch_sha256,
|
|
869
|
+
"minimum_exposures": [
|
|
870
|
+
{"group_id": group_id, "minimum": minimum}
|
|
871
|
+
for group_id, minimum in self.minimum_exposures
|
|
872
|
+
],
|
|
873
|
+
"base_group_counts": {
|
|
874
|
+
group_id: sum(
|
|
875
|
+
group_by_action[action_sha256] == group_id
|
|
876
|
+
for action_sha256
|
|
877
|
+
in base.required_action_sha256s
|
|
878
|
+
)
|
|
879
|
+
for group_id in sorted(set(group_by_action.values()))
|
|
880
|
+
},
|
|
881
|
+
"final_group_counts": {
|
|
882
|
+
group_id: final_counts[group_id]
|
|
883
|
+
for group_id in sorted(final_counts)
|
|
884
|
+
},
|
|
885
|
+
"replacements": replacements,
|
|
886
|
+
"selection_trace": selection_trace,
|
|
887
|
+
"candidate_outcomes_observed": False,
|
|
888
|
+
"workload_model_provider_branches": False,
|
|
889
|
+
}
|
|
890
|
+
),
|
|
891
|
+
)
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
__all__ = [
|
|
895
|
+
"EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_ID",
|
|
896
|
+
"EXPLICIT_EXPERT_SOURCE_GROUP_PROJECTION_VERSION",
|
|
897
|
+
"SOURCE_EXPOSURE_ALLOCATION_POLICY_ID",
|
|
898
|
+
"SOURCE_EXPOSURE_ALLOCATION_POLICY_VERSION",
|
|
899
|
+
"MINIMUM_EXPERT_SOURCE_EXPOSURE_SLATE_FEASIBILITY_VERSION",
|
|
900
|
+
"ExplicitExpertSourceGroupProjection",
|
|
901
|
+
"MaterializedActionSourceGroup",
|
|
902
|
+
"MaterializedActionSourceGroupBatch",
|
|
903
|
+
"MaterializedActionSourceGroupProjectionPort",
|
|
904
|
+
"MinimumSourceExposureAllocationPolicy",
|
|
905
|
+
"MinimumExpertSourceExposureSlateFeasibility",
|
|
906
|
+
]
|