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,1580 @@
|
|
|
1
|
+
"""Outcome-blind paired audits for one frozen adaptive-action prefix.
|
|
2
|
+
|
|
3
|
+
This module is an experimental interception primitive, not a production
|
|
4
|
+
allocation policy. It converts the final factor-stratified audit decision
|
|
5
|
+
into two distinct arms:
|
|
6
|
+
|
|
7
|
+
* the frozen legacy audit anchor; and
|
|
8
|
+
* one factor-stratified alternative selected without either arm's outcome.
|
|
9
|
+
|
|
10
|
+
Callers must durably commit the returned plan before evaluating either arm.
|
|
11
|
+
Both arms are then valued independently against the same pre-audit evaluation
|
|
12
|
+
prefix. Their union is never a budget-matched optimizer result and must not be
|
|
13
|
+
published to the authoritative archive.
|
|
14
|
+
|
|
15
|
+
The design consumes only portable action descriptors and authenticated racing
|
|
16
|
+
evidence. It has no workload, objective, configuration, prompt, model, or
|
|
17
|
+
provider branch.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import hashlib
|
|
23
|
+
import json
|
|
24
|
+
import math
|
|
25
|
+
import re
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from enum import Enum
|
|
28
|
+
from typing import Protocol, runtime_checkable
|
|
29
|
+
|
|
30
|
+
from agent_evolve.application.outcome_adaptive_action_racing import (
|
|
31
|
+
AdaptiveActionDescriptor,
|
|
32
|
+
AdaptiveActionOutcome,
|
|
33
|
+
AdaptiveActionRacingDecision,
|
|
34
|
+
AdaptiveActionSetOutcome,
|
|
35
|
+
AdaptiveActionWave,
|
|
36
|
+
OUTCOME_ADAPTIVE_ACTION_RACING_POLICY_ID,
|
|
37
|
+
OUTCOME_ADAPTIVE_ACTION_RACING_STRATIFIED_AUDIT_POLICY_VERSION,
|
|
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
|
+
|
|
48
|
+
SAME_PREFIX_PAIRED_AUDIT_DESIGNER_ID = (
|
|
49
|
+
"factor_stratified_same_prefix_paired_audit"
|
|
50
|
+
)
|
|
51
|
+
SAME_PREFIX_PAIRED_AUDIT_DESIGNER_VERSION = 1
|
|
52
|
+
FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_ID = (
|
|
53
|
+
"forecast_opportunity_same_prefix_shadow"
|
|
54
|
+
)
|
|
55
|
+
FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_VERSION = 1
|
|
56
|
+
FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_ID = (
|
|
57
|
+
"forecast_stratified_same_prefix_audit"
|
|
58
|
+
)
|
|
59
|
+
FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_VERSION = 1
|
|
60
|
+
FORECAST_OPPORTUNITY_SAME_PREFIX_AUDIT_DESIGNER_IDS = frozenset(
|
|
61
|
+
{
|
|
62
|
+
FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_ID,
|
|
63
|
+
FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_ID,
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_ID = (
|
|
67
|
+
"same_prefix_paired_audit_adjudicator"
|
|
68
|
+
)
|
|
69
|
+
SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_VERSION = 1
|
|
70
|
+
|
|
71
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
72
|
+
_DESIGNER_DEFINITION_DOMAIN = (
|
|
73
|
+
b"agent-evolve:same-prefix-paired-audit-designer-definition:v1\x00"
|
|
74
|
+
)
|
|
75
|
+
_FORECAST_SHADOW_DESIGNER_DEFINITION_DOMAIN = (
|
|
76
|
+
b"agent-evolve:forecast-opportunity-same-prefix-shadow-designer:"
|
|
77
|
+
b"definition:v1\x00"
|
|
78
|
+
)
|
|
79
|
+
_FORECAST_STRATIFIED_DESIGNER_DEFINITION_DOMAIN = (
|
|
80
|
+
b"agent-evolve:forecast-stratified-same-prefix-audit-designer:"
|
|
81
|
+
b"definition:v1\x00"
|
|
82
|
+
)
|
|
83
|
+
_PLAN_DOMAIN = b"agent-evolve:same-prefix-paired-audit-plan:v1\x00"
|
|
84
|
+
_ADJUDICATOR_DEFINITION_DOMAIN = (
|
|
85
|
+
b"agent-evolve:same-prefix-paired-audit-adjudicator-definition:v1\x00"
|
|
86
|
+
)
|
|
87
|
+
_OBSERVATION_DOMAIN = (
|
|
88
|
+
b"agent-evolve:same-prefix-paired-audit-observation:v1\x00"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _canonical_json(value: object) -> bytes:
|
|
93
|
+
return json.dumps(
|
|
94
|
+
value,
|
|
95
|
+
allow_nan=False,
|
|
96
|
+
ensure_ascii=True,
|
|
97
|
+
separators=(",", ":"),
|
|
98
|
+
sort_keys=True,
|
|
99
|
+
).encode("ascii", errors="strict")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
103
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _require_token(value: str, *, name: str) -> None:
|
|
107
|
+
if type(value) is not str or _TOKEN.fullmatch(value) is None:
|
|
108
|
+
raise ValueError(f"{name} must use the closed token grammar")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _require_probability(value: float, *, name: str) -> None:
|
|
112
|
+
if (
|
|
113
|
+
type(value) is not float
|
|
114
|
+
or not math.isfinite(value)
|
|
115
|
+
or not 0.0 < value <= 1.0
|
|
116
|
+
):
|
|
117
|
+
raise ValueError(f"{name} must be a finite positive probability")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _read_nonnegative_hex(record: dict[str, object], key: str) -> float:
|
|
121
|
+
raw = record.get(key)
|
|
122
|
+
if type(raw) is not str:
|
|
123
|
+
raise TypeError(f"{key} must be an exact hexadecimal float")
|
|
124
|
+
try:
|
|
125
|
+
value = float.fromhex(raw)
|
|
126
|
+
except ValueError as error:
|
|
127
|
+
raise ValueError(f"{key} is not a hexadecimal float") from error
|
|
128
|
+
if not math.isfinite(value) or value < 0.0:
|
|
129
|
+
raise ValueError(f"{key} must be finite and non-negative")
|
|
130
|
+
return float(value)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _stable_unit_interval(*parts: object) -> float:
|
|
134
|
+
payload = _canonical_json(list(parts))
|
|
135
|
+
numerator = int.from_bytes(hashlib.sha256(payload).digest()[:8], "big")
|
|
136
|
+
return numerator / float(2**64)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _canonical_hash_tuple(
|
|
140
|
+
values: tuple[str, ...],
|
|
141
|
+
*,
|
|
142
|
+
name: str,
|
|
143
|
+
allow_empty: bool,
|
|
144
|
+
) -> tuple[str, ...]:
|
|
145
|
+
if (
|
|
146
|
+
type(values) is not tuple
|
|
147
|
+
or (not allow_empty and not values)
|
|
148
|
+
or values != tuple(sorted(set(values)))
|
|
149
|
+
):
|
|
150
|
+
raise ValueError(f"{name} must be a canonical exact tuple")
|
|
151
|
+
for value in values:
|
|
152
|
+
require_sha256(value, name)
|
|
153
|
+
return values
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class SamePrefixPairedAuditArm(str, Enum):
|
|
157
|
+
"""The arm retained by the original budget-matched decision."""
|
|
158
|
+
|
|
159
|
+
LEGACY = "legacy"
|
|
160
|
+
EXPLORATION = "exploration"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class SamePrefixPairedAuditWinner(str, Enum):
|
|
164
|
+
"""Winner under conditional utility at the common prefix."""
|
|
165
|
+
|
|
166
|
+
LEGACY = "legacy"
|
|
167
|
+
EXPLORATION = "exploration"
|
|
168
|
+
TIE = "tie"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@dataclass(frozen=True, slots=True)
|
|
172
|
+
class SamePrefixPairedAuditPlan:
|
|
173
|
+
"""Hash-bound plan that must be committed before arm evaluation."""
|
|
174
|
+
|
|
175
|
+
designer_id: str
|
|
176
|
+
designer_version: int
|
|
177
|
+
designer_definition_sha256: str
|
|
178
|
+
residual_request_sha256: str
|
|
179
|
+
racing_decision_sha256: str
|
|
180
|
+
common_prefix_action_sha256s: tuple[str, ...]
|
|
181
|
+
authoritative_arm: SamePrefixPairedAuditArm
|
|
182
|
+
authoritative_action_sha256: str
|
|
183
|
+
legacy_action_sha256: str
|
|
184
|
+
exploration_action_sha256: str
|
|
185
|
+
exploration_stratum_key: tuple[str, ...]
|
|
186
|
+
distinct_exploration_support_action_sha256s: tuple[str, ...]
|
|
187
|
+
exploration_selection_propensity: float
|
|
188
|
+
evidence: FrozenJsonObject
|
|
189
|
+
plan_sha256: str = field(init=False)
|
|
190
|
+
|
|
191
|
+
def __post_init__(self) -> None:
|
|
192
|
+
_require_token(self.designer_id, name="designer_id")
|
|
193
|
+
if type(self.designer_version) is not int or self.designer_version <= 0:
|
|
194
|
+
raise ValueError("designer_version must be positive")
|
|
195
|
+
for value, name in (
|
|
196
|
+
(
|
|
197
|
+
self.designer_definition_sha256,
|
|
198
|
+
"designer_definition_sha256",
|
|
199
|
+
),
|
|
200
|
+
(self.residual_request_sha256, "residual_request_sha256"),
|
|
201
|
+
(self.racing_decision_sha256, "racing_decision_sha256"),
|
|
202
|
+
(self.authoritative_action_sha256, "authoritative_action_sha256"),
|
|
203
|
+
(self.legacy_action_sha256, "legacy_action_sha256"),
|
|
204
|
+
(self.exploration_action_sha256, "exploration_action_sha256"),
|
|
205
|
+
):
|
|
206
|
+
require_sha256(value, name)
|
|
207
|
+
prefix = _canonical_hash_tuple(
|
|
208
|
+
self.common_prefix_action_sha256s,
|
|
209
|
+
name="common_prefix_action_sha256s",
|
|
210
|
+
allow_empty=False,
|
|
211
|
+
)
|
|
212
|
+
support = _canonical_hash_tuple(
|
|
213
|
+
self.distinct_exploration_support_action_sha256s,
|
|
214
|
+
name="distinct_exploration_support_action_sha256s",
|
|
215
|
+
allow_empty=False,
|
|
216
|
+
)
|
|
217
|
+
if type(self.authoritative_arm) is not SamePrefixPairedAuditArm:
|
|
218
|
+
raise TypeError("authoritative_arm must be exact")
|
|
219
|
+
if self.legacy_action_sha256 == self.exploration_action_sha256:
|
|
220
|
+
raise ValueError("paired audit arms must be distinct")
|
|
221
|
+
if {
|
|
222
|
+
self.legacy_action_sha256,
|
|
223
|
+
self.exploration_action_sha256,
|
|
224
|
+
} & set(prefix):
|
|
225
|
+
raise ValueError("paired audit arm is already in the common prefix")
|
|
226
|
+
if self.exploration_action_sha256 not in support:
|
|
227
|
+
raise ValueError("exploration arm is outside its distinct support")
|
|
228
|
+
expected_authoritative = (
|
|
229
|
+
self.legacy_action_sha256
|
|
230
|
+
if self.authoritative_arm is SamePrefixPairedAuditArm.LEGACY
|
|
231
|
+
else self.exploration_action_sha256
|
|
232
|
+
)
|
|
233
|
+
if self.authoritative_action_sha256 != expected_authoritative:
|
|
234
|
+
raise ValueError("authoritative action does not match its arm")
|
|
235
|
+
if (
|
|
236
|
+
type(self.exploration_stratum_key) is not tuple
|
|
237
|
+
or not self.exploration_stratum_key
|
|
238
|
+
):
|
|
239
|
+
raise ValueError("exploration_stratum_key must be non-empty")
|
|
240
|
+
for value in self.exploration_stratum_key:
|
|
241
|
+
_require_token(value, name="exploration stratum level")
|
|
242
|
+
_require_probability(
|
|
243
|
+
self.exploration_selection_propensity,
|
|
244
|
+
name="exploration_selection_propensity",
|
|
245
|
+
)
|
|
246
|
+
if (
|
|
247
|
+
type(self.evidence) is not FrozenJsonObject
|
|
248
|
+
or freeze_json(self.evidence) is not self.evidence
|
|
249
|
+
):
|
|
250
|
+
raise TypeError("evidence must be an exact frozen object")
|
|
251
|
+
object.__setattr__(
|
|
252
|
+
self,
|
|
253
|
+
"plan_sha256",
|
|
254
|
+
_hash(_PLAN_DOMAIN, self._unsigned_record()),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
258
|
+
return {
|
|
259
|
+
"schema_version": 1,
|
|
260
|
+
"designer": {
|
|
261
|
+
"designer_id": self.designer_id,
|
|
262
|
+
"designer_version": self.designer_version,
|
|
263
|
+
"definition_sha256": self.designer_definition_sha256,
|
|
264
|
+
},
|
|
265
|
+
"residual_request_sha256": self.residual_request_sha256,
|
|
266
|
+
"racing_decision_sha256": self.racing_decision_sha256,
|
|
267
|
+
"common_prefix_action_sha256s": list(
|
|
268
|
+
self.common_prefix_action_sha256s
|
|
269
|
+
),
|
|
270
|
+
"authoritative_arm": self.authoritative_arm.value,
|
|
271
|
+
"authoritative_action_sha256": (
|
|
272
|
+
self.authoritative_action_sha256
|
|
273
|
+
),
|
|
274
|
+
"legacy_action_sha256": self.legacy_action_sha256,
|
|
275
|
+
"exploration_action_sha256": self.exploration_action_sha256,
|
|
276
|
+
"exploration_stratum_key": list(
|
|
277
|
+
self.exploration_stratum_key
|
|
278
|
+
),
|
|
279
|
+
"distinct_exploration_support_action_sha256s": list(
|
|
280
|
+
self.distinct_exploration_support_action_sha256s
|
|
281
|
+
),
|
|
282
|
+
"exploration_selection_propensity_hex": (
|
|
283
|
+
self.exploration_selection_propensity.hex()
|
|
284
|
+
),
|
|
285
|
+
"evidence_sha256": typed_json_sha256(self.evidence),
|
|
286
|
+
"current_arm_outcomes_observed": False,
|
|
287
|
+
"assay_union_may_enter_authoritative_archive": False,
|
|
288
|
+
"workload_objective_model_provider_prompt_config_branches": False,
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
def to_record(self, *, include_evidence: bool = False) -> dict[str, object]:
|
|
292
|
+
self.__post_init__()
|
|
293
|
+
result = {
|
|
294
|
+
**self._unsigned_record(),
|
|
295
|
+
"plan_sha256": self.plan_sha256,
|
|
296
|
+
}
|
|
297
|
+
if include_evidence:
|
|
298
|
+
result["evidence"] = thaw_json(self.evidence)
|
|
299
|
+
return result
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@runtime_checkable
|
|
303
|
+
class SamePrefixPairedAuditDesignerPort(Protocol):
|
|
304
|
+
"""Inverted port for an outcome-blind paired-audit design."""
|
|
305
|
+
|
|
306
|
+
designer_id: str
|
|
307
|
+
designer_version: int
|
|
308
|
+
definition_sha256: str
|
|
309
|
+
|
|
310
|
+
def design(
|
|
311
|
+
self,
|
|
312
|
+
*,
|
|
313
|
+
decision: AdaptiveActionRacingDecision,
|
|
314
|
+
actions: tuple[AdaptiveActionDescriptor, ...],
|
|
315
|
+
) -> SamePrefixPairedAuditPlan: ...
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@runtime_checkable
|
|
319
|
+
class ForecastOpportunitySamePrefixShadowDesignerPort(Protocol):
|
|
320
|
+
"""Inverted port for a pre-outcome challenger-versus-fallback shadow."""
|
|
321
|
+
|
|
322
|
+
designer_id: str
|
|
323
|
+
designer_version: int
|
|
324
|
+
definition_sha256: str
|
|
325
|
+
|
|
326
|
+
def design(
|
|
327
|
+
self,
|
|
328
|
+
*,
|
|
329
|
+
adaptive_step: int,
|
|
330
|
+
remaining_authoritative_slots_after_decision: int,
|
|
331
|
+
decision: AdaptiveActionRacingDecision,
|
|
332
|
+
fallback: AdaptiveActionRacingDecision,
|
|
333
|
+
actions: tuple[AdaptiveActionDescriptor, ...],
|
|
334
|
+
) -> SamePrefixPairedAuditPlan | None: ...
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
ForecastOpportunitySamePrefixAuditDesignerPort = (
|
|
338
|
+
ForecastOpportunitySamePrefixShadowDesignerPort
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
@dataclass(frozen=True, slots=True)
|
|
343
|
+
class ForecastOpportunitySamePrefixShadowDesigner:
|
|
344
|
+
"""Freeze a final-step forecast challenger against its exact fallback.
|
|
345
|
+
|
|
346
|
+
Final-step interception prevents a shadow outcome from becoming a later
|
|
347
|
+
authoritative action in the same stage. Only the challenger remains in
|
|
348
|
+
the budget-matched optimizer archive.
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
final_continuation_only: bool = True
|
|
352
|
+
designer_id: str = (
|
|
353
|
+
FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_ID
|
|
354
|
+
)
|
|
355
|
+
designer_version: int = (
|
|
356
|
+
FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_VERSION
|
|
357
|
+
)
|
|
358
|
+
definition_sha256: str = field(init=False)
|
|
359
|
+
|
|
360
|
+
def __post_init__(self) -> None:
|
|
361
|
+
if type(self.final_continuation_only) is not bool:
|
|
362
|
+
raise TypeError("final_continuation_only must be exact")
|
|
363
|
+
if (
|
|
364
|
+
self.designer_id
|
|
365
|
+
!= FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_ID
|
|
366
|
+
or self.designer_version
|
|
367
|
+
!= FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_VERSION
|
|
368
|
+
):
|
|
369
|
+
raise ValueError("forecast shadow designer identity is immutable")
|
|
370
|
+
object.__setattr__(
|
|
371
|
+
self,
|
|
372
|
+
"definition_sha256",
|
|
373
|
+
_hash(
|
|
374
|
+
_FORECAST_SHADOW_DESIGNER_DEFINITION_DOMAIN,
|
|
375
|
+
{
|
|
376
|
+
"schema_version": 1,
|
|
377
|
+
"designer_id": self.designer_id,
|
|
378
|
+
"designer_version": self.designer_version,
|
|
379
|
+
"final_continuation_only": self.final_continuation_only,
|
|
380
|
+
"authoritative_arm": "forecast_opportunity_challenger",
|
|
381
|
+
"counterfactual_arm": "authenticated_fallback",
|
|
382
|
+
"common_prefix": "exact-real-selected-prefix",
|
|
383
|
+
"plan_frozen_before_either_arm_outcome": True,
|
|
384
|
+
"assay_union_may_enter_authoritative_archive": False,
|
|
385
|
+
"workload_objective_model_provider_prompt_config_"
|
|
386
|
+
"branches": False,
|
|
387
|
+
},
|
|
388
|
+
),
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
@staticmethod
|
|
392
|
+
def _validate_action_market(
|
|
393
|
+
actions: tuple[AdaptiveActionDescriptor, ...],
|
|
394
|
+
) -> dict[str, AdaptiveActionDescriptor]:
|
|
395
|
+
if type(actions) is not tuple or not actions:
|
|
396
|
+
raise ValueError("actions must be a non-empty exact tuple")
|
|
397
|
+
action_by_sha256: dict[str, AdaptiveActionDescriptor] = {}
|
|
398
|
+
for value in actions:
|
|
399
|
+
if type(value) is not AdaptiveActionDescriptor:
|
|
400
|
+
raise TypeError("actions must contain exact descriptors")
|
|
401
|
+
value.__post_init__()
|
|
402
|
+
if value.action_sha256 in action_by_sha256:
|
|
403
|
+
raise ValueError("actions repeat an identity")
|
|
404
|
+
action_by_sha256[value.action_sha256] = value
|
|
405
|
+
return action_by_sha256
|
|
406
|
+
|
|
407
|
+
def design(
|
|
408
|
+
self,
|
|
409
|
+
*,
|
|
410
|
+
adaptive_step: int,
|
|
411
|
+
remaining_authoritative_slots_after_decision: int,
|
|
412
|
+
decision: AdaptiveActionRacingDecision,
|
|
413
|
+
fallback: AdaptiveActionRacingDecision,
|
|
414
|
+
actions: tuple[AdaptiveActionDescriptor, ...],
|
|
415
|
+
) -> SamePrefixPairedAuditPlan | None:
|
|
416
|
+
"""Return no assay for abstention, unchanged action, or an early step."""
|
|
417
|
+
|
|
418
|
+
self.__post_init__()
|
|
419
|
+
if type(adaptive_step) is not int or adaptive_step <= 0:
|
|
420
|
+
raise ValueError("adaptive_step must be positive")
|
|
421
|
+
if (
|
|
422
|
+
type(remaining_authoritative_slots_after_decision) is not int
|
|
423
|
+
or remaining_authoritative_slots_after_decision < 0
|
|
424
|
+
):
|
|
425
|
+
raise ValueError(
|
|
426
|
+
"remaining_authoritative_slots_after_decision must be "
|
|
427
|
+
"non-negative"
|
|
428
|
+
)
|
|
429
|
+
if (
|
|
430
|
+
self.final_continuation_only
|
|
431
|
+
and remaining_authoritative_slots_after_decision != 0
|
|
432
|
+
):
|
|
433
|
+
return None
|
|
434
|
+
if type(decision) is not AdaptiveActionRacingDecision:
|
|
435
|
+
raise TypeError("decision must be exact")
|
|
436
|
+
if type(fallback) is not AdaptiveActionRacingDecision:
|
|
437
|
+
raise TypeError("fallback must be exact")
|
|
438
|
+
decision.__post_init__()
|
|
439
|
+
fallback.__post_init__()
|
|
440
|
+
if (
|
|
441
|
+
decision.wave is AdaptiveActionWave.DIAGNOSTIC
|
|
442
|
+
or fallback.wave is AdaptiveActionWave.DIAGNOSTIC
|
|
443
|
+
or decision.wave is not fallback.wave
|
|
444
|
+
):
|
|
445
|
+
raise ValueError("forecast shadow requires one continuation wave")
|
|
446
|
+
if (
|
|
447
|
+
len(decision.selected_action_sha256s) != 1
|
|
448
|
+
or len(fallback.selected_action_sha256s) != 1
|
|
449
|
+
):
|
|
450
|
+
raise ValueError("forecast shadow arms must each select one action")
|
|
451
|
+
if (
|
|
452
|
+
decision.residual_request_sha256
|
|
453
|
+
!= fallback.residual_request_sha256
|
|
454
|
+
or decision.prior_selected_action_sha256s
|
|
455
|
+
!= fallback.prior_selected_action_sha256s
|
|
456
|
+
or decision.observed_outcome_sha256s
|
|
457
|
+
!= fallback.observed_outcome_sha256s
|
|
458
|
+
or decision.observed_set_outcome_sha256s
|
|
459
|
+
!= fallback.observed_set_outcome_sha256s
|
|
460
|
+
):
|
|
461
|
+
raise ValueError("forecast challenger and fallback cutoffs differ")
|
|
462
|
+
challenger_action_sha256 = decision.selected_action_sha256s[0]
|
|
463
|
+
fallback_action_sha256 = fallback.selected_action_sha256s[0]
|
|
464
|
+
if challenger_action_sha256 == fallback_action_sha256:
|
|
465
|
+
return None
|
|
466
|
+
action_by_sha256 = self._validate_action_market(actions)
|
|
467
|
+
prefix = set(decision.prior_selected_action_sha256s)
|
|
468
|
+
if (
|
|
469
|
+
not prefix
|
|
470
|
+
or not prefix.issubset(action_by_sha256)
|
|
471
|
+
or challenger_action_sha256 not in action_by_sha256
|
|
472
|
+
or fallback_action_sha256 not in action_by_sha256
|
|
473
|
+
or {
|
|
474
|
+
challenger_action_sha256,
|
|
475
|
+
fallback_action_sha256,
|
|
476
|
+
}
|
|
477
|
+
& prefix
|
|
478
|
+
):
|
|
479
|
+
raise ValueError("forecast shadow arms are outside the open market")
|
|
480
|
+
evidence = thaw_json(decision.evidence)
|
|
481
|
+
if (
|
|
482
|
+
evidence.get("selection_source")
|
|
483
|
+
!= "current_prefix_forecast_opportunity"
|
|
484
|
+
or evidence.get("fallback_preserved_on_abstention") is not True
|
|
485
|
+
or evidence.get("eligible_candidate_outcomes_observed") is not False
|
|
486
|
+
):
|
|
487
|
+
raise ValueError(
|
|
488
|
+
"decision lacks the protected forecast-opportunity contract"
|
|
489
|
+
)
|
|
490
|
+
embedded_fallback = evidence.get("fallback_decision")
|
|
491
|
+
expected_fallback = fallback.to_record(include_evidence=True)
|
|
492
|
+
if embedded_fallback != expected_fallback:
|
|
493
|
+
raise ValueError(
|
|
494
|
+
"decision does not authenticate the supplied fallback"
|
|
495
|
+
)
|
|
496
|
+
return SamePrefixPairedAuditPlan(
|
|
497
|
+
designer_id=self.designer_id,
|
|
498
|
+
designer_version=self.designer_version,
|
|
499
|
+
designer_definition_sha256=self.definition_sha256,
|
|
500
|
+
residual_request_sha256=decision.residual_request_sha256,
|
|
501
|
+
racing_decision_sha256=decision.decision_sha256,
|
|
502
|
+
common_prefix_action_sha256s=(
|
|
503
|
+
decision.prior_selected_action_sha256s
|
|
504
|
+
),
|
|
505
|
+
authoritative_arm=SamePrefixPairedAuditArm.EXPLORATION,
|
|
506
|
+
authoritative_action_sha256=challenger_action_sha256,
|
|
507
|
+
legacy_action_sha256=fallback_action_sha256,
|
|
508
|
+
exploration_action_sha256=challenger_action_sha256,
|
|
509
|
+
exploration_stratum_key=(
|
|
510
|
+
"selection_source",
|
|
511
|
+
"forecast_opportunity",
|
|
512
|
+
),
|
|
513
|
+
distinct_exploration_support_action_sha256s=(
|
|
514
|
+
challenger_action_sha256,
|
|
515
|
+
),
|
|
516
|
+
exploration_selection_propensity=1.0,
|
|
517
|
+
evidence=freeze_json(
|
|
518
|
+
{
|
|
519
|
+
"adaptive_step": adaptive_step,
|
|
520
|
+
"remaining_authoritative_slots_after_decision": (
|
|
521
|
+
remaining_authoritative_slots_after_decision
|
|
522
|
+
),
|
|
523
|
+
"challenger_decision_sha256": decision.decision_sha256,
|
|
524
|
+
"fallback_decision_sha256": fallback.decision_sha256,
|
|
525
|
+
"challenger_action_sha256": challenger_action_sha256,
|
|
526
|
+
"fallback_action_sha256": fallback_action_sha256,
|
|
527
|
+
"legacy_arm_semantic_role": "authenticated_fallback",
|
|
528
|
+
"exploration_arm_semantic_role": (
|
|
529
|
+
"forecast_opportunity_challenger"
|
|
530
|
+
),
|
|
531
|
+
"final_continuation_only": (
|
|
532
|
+
self.final_continuation_only
|
|
533
|
+
),
|
|
534
|
+
"current_arm_outcomes_observed": False,
|
|
535
|
+
"plan_must_be_committed_before_arm_evaluation": True,
|
|
536
|
+
"assay_union_may_enter_authoritative_archive": False,
|
|
537
|
+
}
|
|
538
|
+
),
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
@dataclass(frozen=True, slots=True)
|
|
543
|
+
class ForecastStratifiedSamePrefixAuditDesigner:
|
|
544
|
+
"""Acquire one same-prefix forecast audit even when CPO abstains.
|
|
545
|
+
|
|
546
|
+
One continuation position is selected uniformly and deterministically from
|
|
547
|
+
the opaque residual-request identity. At that position, an unchanged
|
|
548
|
+
protected fallback remains authoritative while a forecast-covered action
|
|
549
|
+
is sampled by a uniform-nonempty-stratum, uniform-within-stratum design.
|
|
550
|
+
If the protected challenger already changed fallback, its selected action
|
|
551
|
+
is reused as the exploration arm. Neither arm outcome is available while
|
|
552
|
+
the plan is constructed.
|
|
553
|
+
"""
|
|
554
|
+
|
|
555
|
+
random_seed: int = 0
|
|
556
|
+
designer_id: str = (
|
|
557
|
+
FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_ID
|
|
558
|
+
)
|
|
559
|
+
designer_version: int = (
|
|
560
|
+
FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_VERSION
|
|
561
|
+
)
|
|
562
|
+
definition_sha256: str = field(init=False)
|
|
563
|
+
|
|
564
|
+
def __post_init__(self) -> None:
|
|
565
|
+
if type(self.random_seed) is not int or self.random_seed < 0:
|
|
566
|
+
raise ValueError("random_seed must be non-negative")
|
|
567
|
+
if (
|
|
568
|
+
self.designer_id
|
|
569
|
+
!= FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_ID
|
|
570
|
+
or self.designer_version
|
|
571
|
+
!= FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_VERSION
|
|
572
|
+
):
|
|
573
|
+
raise ValueError(
|
|
574
|
+
"forecast-stratified audit designer identity is immutable"
|
|
575
|
+
)
|
|
576
|
+
object.__setattr__(
|
|
577
|
+
self,
|
|
578
|
+
"definition_sha256",
|
|
579
|
+
_hash(
|
|
580
|
+
_FORECAST_STRATIFIED_DESIGNER_DEFINITION_DOMAIN,
|
|
581
|
+
{
|
|
582
|
+
"schema_version": 1,
|
|
583
|
+
"designer_id": self.designer_id,
|
|
584
|
+
"designer_version": self.designer_version,
|
|
585
|
+
"random_seed": self.random_seed,
|
|
586
|
+
"schedule": (
|
|
587
|
+
"one-hash-uniform-continuation-position-per-request"
|
|
588
|
+
),
|
|
589
|
+
"strata": [
|
|
590
|
+
"recommended",
|
|
591
|
+
"adverse_positive",
|
|
592
|
+
"central_positive_adverse_zero",
|
|
593
|
+
"favorable_positive_central_zero",
|
|
594
|
+
"forecast_zero",
|
|
595
|
+
],
|
|
596
|
+
"sampling": (
|
|
597
|
+
"uniform-nonempty-stratum-then-uniform-action"
|
|
598
|
+
),
|
|
599
|
+
"abstention_authoritative_arm": "protected_fallback",
|
|
600
|
+
"intervention_authoritative_arm": (
|
|
601
|
+
"forecast_opportunity_challenger"
|
|
602
|
+
),
|
|
603
|
+
"counterfactual_action_quarantined_after_assay": True,
|
|
604
|
+
"plan_frozen_before_either_arm_outcome": True,
|
|
605
|
+
"assay_union_may_enter_authoritative_archive": False,
|
|
606
|
+
"workload_objective_model_provider_prompt_config_"
|
|
607
|
+
"branches": False,
|
|
608
|
+
},
|
|
609
|
+
),
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
@staticmethod
|
|
613
|
+
def _validate_action_market(
|
|
614
|
+
actions: tuple[AdaptiveActionDescriptor, ...],
|
|
615
|
+
) -> dict[str, AdaptiveActionDescriptor]:
|
|
616
|
+
if type(actions) is not tuple or not actions:
|
|
617
|
+
raise ValueError("actions must be a non-empty exact tuple")
|
|
618
|
+
action_by_sha256: dict[str, AdaptiveActionDescriptor] = {}
|
|
619
|
+
for value in actions:
|
|
620
|
+
if type(value) is not AdaptiveActionDescriptor:
|
|
621
|
+
raise TypeError("actions must contain exact descriptors")
|
|
622
|
+
value.__post_init__()
|
|
623
|
+
if value.action_sha256 in action_by_sha256:
|
|
624
|
+
raise ValueError("actions repeat an identity")
|
|
625
|
+
action_by_sha256[value.action_sha256] = value
|
|
626
|
+
return action_by_sha256
|
|
627
|
+
|
|
628
|
+
@staticmethod
|
|
629
|
+
def _score_stratum(
|
|
630
|
+
*,
|
|
631
|
+
score: dict[str, object],
|
|
632
|
+
recommended_action_sha256s: set[str],
|
|
633
|
+
) -> str:
|
|
634
|
+
action_sha256 = score.get("action_sha256")
|
|
635
|
+
if type(action_sha256) is not str:
|
|
636
|
+
raise TypeError("opportunity score lacks an action identity")
|
|
637
|
+
if action_sha256 in recommended_action_sha256s:
|
|
638
|
+
return "recommended"
|
|
639
|
+
adverse = _read_nonnegative_hex(score, "adverse_gain_hex")
|
|
640
|
+
central = _read_nonnegative_hex(score, "central_gain_hex")
|
|
641
|
+
favorable = _read_nonnegative_hex(score, "favorable_gain_hex")
|
|
642
|
+
if adverse > 0.0:
|
|
643
|
+
return "adverse_positive"
|
|
644
|
+
if central > 0.0:
|
|
645
|
+
return "central_positive_adverse_zero"
|
|
646
|
+
if favorable > 0.0:
|
|
647
|
+
return "favorable_positive_central_zero"
|
|
648
|
+
return "forecast_zero"
|
|
649
|
+
|
|
650
|
+
@classmethod
|
|
651
|
+
def _read_forecast_strata(
|
|
652
|
+
cls,
|
|
653
|
+
*,
|
|
654
|
+
evidence: dict[str, object],
|
|
655
|
+
action_by_sha256: dict[str, AdaptiveActionDescriptor],
|
|
656
|
+
prefix: set[str],
|
|
657
|
+
legacy_action_sha256: str,
|
|
658
|
+
) -> tuple[
|
|
659
|
+
tuple[str, tuple[str, ...]],
|
|
660
|
+
...,
|
|
661
|
+
]:
|
|
662
|
+
ranking = evidence.get("opportunity_ranking")
|
|
663
|
+
if type(ranking) is not dict:
|
|
664
|
+
raise TypeError("decision lacks an opportunity ranking")
|
|
665
|
+
if (
|
|
666
|
+
ranking.get("eligible_candidate_outcomes_observed") is not False
|
|
667
|
+
):
|
|
668
|
+
raise ValueError(
|
|
669
|
+
"forecast audit ranking observed eligible outcomes"
|
|
670
|
+
)
|
|
671
|
+
raw_recommended = ranking.get("recommended_action_sha256s")
|
|
672
|
+
raw_eligible = ranking.get("eligible_action_sha256s")
|
|
673
|
+
raw_scores = ranking.get("scores")
|
|
674
|
+
if (
|
|
675
|
+
type(raw_recommended) is not list
|
|
676
|
+
or type(raw_eligible) is not list
|
|
677
|
+
or type(raw_scores) is not list
|
|
678
|
+
):
|
|
679
|
+
raise TypeError("opportunity ranking is incomplete")
|
|
680
|
+
recommended = set(raw_recommended)
|
|
681
|
+
eligible = tuple(raw_eligible)
|
|
682
|
+
if (
|
|
683
|
+
len(recommended) != len(raw_recommended)
|
|
684
|
+
or eligible != tuple(sorted(set(eligible)))
|
|
685
|
+
or not recommended <= set(eligible)
|
|
686
|
+
):
|
|
687
|
+
raise ValueError("opportunity ranking support is not canonical")
|
|
688
|
+
grouped: dict[str, list[str]] = {}
|
|
689
|
+
seen_scores: set[str] = set()
|
|
690
|
+
for raw_score in raw_scores:
|
|
691
|
+
if type(raw_score) is not dict:
|
|
692
|
+
raise TypeError("opportunity score must be an exact object")
|
|
693
|
+
action_sha256 = raw_score.get("action_sha256")
|
|
694
|
+
if type(action_sha256) is not str:
|
|
695
|
+
raise TypeError("opportunity score lacks an action identity")
|
|
696
|
+
require_sha256(action_sha256, "opportunity action_sha256")
|
|
697
|
+
if (
|
|
698
|
+
action_sha256 in seen_scores
|
|
699
|
+
or action_sha256 not in set(eligible)
|
|
700
|
+
):
|
|
701
|
+
raise ValueError(
|
|
702
|
+
"opportunity scores differ from eligible support"
|
|
703
|
+
)
|
|
704
|
+
seen_scores.add(action_sha256)
|
|
705
|
+
if (
|
|
706
|
+
action_sha256 == legacy_action_sha256
|
|
707
|
+
or action_sha256 in prefix
|
|
708
|
+
or action_sha256 not in action_by_sha256
|
|
709
|
+
):
|
|
710
|
+
continue
|
|
711
|
+
stratum = cls._score_stratum(
|
|
712
|
+
score=raw_score,
|
|
713
|
+
recommended_action_sha256s=recommended,
|
|
714
|
+
)
|
|
715
|
+
grouped.setdefault(stratum, []).append(action_sha256)
|
|
716
|
+
if seen_scores != set(eligible):
|
|
717
|
+
raise ValueError(
|
|
718
|
+
"opportunity scores do not cover eligible support"
|
|
719
|
+
)
|
|
720
|
+
return tuple(
|
|
721
|
+
sorted(
|
|
722
|
+
(
|
|
723
|
+
stratum,
|
|
724
|
+
tuple(sorted(action_sha256s)),
|
|
725
|
+
)
|
|
726
|
+
for stratum, action_sha256s in grouped.items()
|
|
727
|
+
if action_sha256s
|
|
728
|
+
)
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
def design(
|
|
732
|
+
self,
|
|
733
|
+
*,
|
|
734
|
+
adaptive_step: int,
|
|
735
|
+
remaining_authoritative_slots_after_decision: int,
|
|
736
|
+
decision: AdaptiveActionRacingDecision,
|
|
737
|
+
fallback: AdaptiveActionRacingDecision,
|
|
738
|
+
actions: tuple[AdaptiveActionDescriptor, ...],
|
|
739
|
+
) -> SamePrefixPairedAuditPlan | None:
|
|
740
|
+
"""Freeze a propensity-logged audit at one rotated position."""
|
|
741
|
+
|
|
742
|
+
self.__post_init__()
|
|
743
|
+
if type(adaptive_step) is not int or adaptive_step <= 0:
|
|
744
|
+
raise ValueError("adaptive_step must be positive")
|
|
745
|
+
if (
|
|
746
|
+
type(remaining_authoritative_slots_after_decision) is not int
|
|
747
|
+
or remaining_authoritative_slots_after_decision < 0
|
|
748
|
+
):
|
|
749
|
+
raise ValueError(
|
|
750
|
+
"remaining_authoritative_slots_after_decision must be "
|
|
751
|
+
"non-negative"
|
|
752
|
+
)
|
|
753
|
+
if type(decision) is not AdaptiveActionRacingDecision:
|
|
754
|
+
raise TypeError("decision must be exact")
|
|
755
|
+
if type(fallback) is not AdaptiveActionRacingDecision:
|
|
756
|
+
raise TypeError("fallback must be exact")
|
|
757
|
+
decision.__post_init__()
|
|
758
|
+
fallback.__post_init__()
|
|
759
|
+
if (
|
|
760
|
+
decision.wave is AdaptiveActionWave.DIAGNOSTIC
|
|
761
|
+
or fallback.wave is AdaptiveActionWave.DIAGNOSTIC
|
|
762
|
+
or decision.wave is not fallback.wave
|
|
763
|
+
or len(decision.selected_action_sha256s) != 1
|
|
764
|
+
or len(fallback.selected_action_sha256s) != 1
|
|
765
|
+
):
|
|
766
|
+
raise ValueError(
|
|
767
|
+
"forecast-stratified audit requires one continuation action"
|
|
768
|
+
)
|
|
769
|
+
if (
|
|
770
|
+
decision.residual_request_sha256
|
|
771
|
+
!= fallback.residual_request_sha256
|
|
772
|
+
or decision.prior_selected_action_sha256s
|
|
773
|
+
!= fallback.prior_selected_action_sha256s
|
|
774
|
+
or decision.observed_outcome_sha256s
|
|
775
|
+
!= fallback.observed_outcome_sha256s
|
|
776
|
+
or decision.observed_set_outcome_sha256s
|
|
777
|
+
!= fallback.observed_set_outcome_sha256s
|
|
778
|
+
):
|
|
779
|
+
raise ValueError("forecast challenger and fallback cutoffs differ")
|
|
780
|
+
total_continuation_count = (
|
|
781
|
+
adaptive_step
|
|
782
|
+
+ remaining_authoritative_slots_after_decision
|
|
783
|
+
)
|
|
784
|
+
position_draw = _stable_unit_interval(
|
|
785
|
+
self.random_seed,
|
|
786
|
+
decision.residual_request_sha256,
|
|
787
|
+
"forecast_stratified_audit_position",
|
|
788
|
+
total_continuation_count,
|
|
789
|
+
)
|
|
790
|
+
target_adaptive_step = 1 + min(
|
|
791
|
+
int(position_draw * total_continuation_count),
|
|
792
|
+
total_continuation_count - 1,
|
|
793
|
+
)
|
|
794
|
+
if adaptive_step != target_adaptive_step:
|
|
795
|
+
return None
|
|
796
|
+
|
|
797
|
+
action_by_sha256 = self._validate_action_market(actions)
|
|
798
|
+
prefix = set(decision.prior_selected_action_sha256s)
|
|
799
|
+
authoritative_action_sha256 = (
|
|
800
|
+
decision.selected_action_sha256s[0]
|
|
801
|
+
)
|
|
802
|
+
legacy_action_sha256 = fallback.selected_action_sha256s[0]
|
|
803
|
+
if (
|
|
804
|
+
not prefix
|
|
805
|
+
or not prefix.issubset(action_by_sha256)
|
|
806
|
+
or authoritative_action_sha256 not in action_by_sha256
|
|
807
|
+
or legacy_action_sha256 not in action_by_sha256
|
|
808
|
+
or authoritative_action_sha256 in prefix
|
|
809
|
+
or legacy_action_sha256 in prefix
|
|
810
|
+
):
|
|
811
|
+
raise ValueError(
|
|
812
|
+
"forecast audit arms are outside the open market"
|
|
813
|
+
)
|
|
814
|
+
# One authoritative action and one quarantined shadow must leave
|
|
815
|
+
# enough distinct actions for every remaining authoritative slot.
|
|
816
|
+
if (
|
|
817
|
+
len(action_by_sha256) - len(prefix)
|
|
818
|
+
< remaining_authoritative_slots_after_decision + 2
|
|
819
|
+
):
|
|
820
|
+
return None
|
|
821
|
+
evidence = thaw_json(decision.evidence)
|
|
822
|
+
if (
|
|
823
|
+
evidence.get("fallback_preserved_on_abstention") is not True
|
|
824
|
+
or evidence.get("eligible_candidate_outcomes_observed") is not False
|
|
825
|
+
or evidence.get("fallback_decision")
|
|
826
|
+
!= fallback.to_record(include_evidence=True)
|
|
827
|
+
):
|
|
828
|
+
raise ValueError(
|
|
829
|
+
"decision lacks the protected forecast-opportunity contract"
|
|
830
|
+
)
|
|
831
|
+
selection_source = evidence.get("selection_source")
|
|
832
|
+
if selection_source not in {
|
|
833
|
+
"current_prefix_forecast_opportunity",
|
|
834
|
+
"protected_fallback",
|
|
835
|
+
}:
|
|
836
|
+
raise ValueError("decision has an unknown selection source")
|
|
837
|
+
if (
|
|
838
|
+
selection_source == "protected_fallback"
|
|
839
|
+
and authoritative_action_sha256 != legacy_action_sha256
|
|
840
|
+
):
|
|
841
|
+
raise ValueError(
|
|
842
|
+
"protected fallback source changed the fallback action"
|
|
843
|
+
)
|
|
844
|
+
strata = self._read_forecast_strata(
|
|
845
|
+
evidence=evidence,
|
|
846
|
+
action_by_sha256=action_by_sha256,
|
|
847
|
+
prefix=prefix,
|
|
848
|
+
legacy_action_sha256=legacy_action_sha256,
|
|
849
|
+
)
|
|
850
|
+
if not strata:
|
|
851
|
+
return None
|
|
852
|
+
stratum_by_action = {
|
|
853
|
+
action_sha256: stratum
|
|
854
|
+
for stratum, action_sha256s in strata
|
|
855
|
+
for action_sha256 in action_sha256s
|
|
856
|
+
}
|
|
857
|
+
reused_challenger = (
|
|
858
|
+
authoritative_action_sha256 != legacy_action_sha256
|
|
859
|
+
)
|
|
860
|
+
stratum_draw: float | None = None
|
|
861
|
+
action_draw: float | None = None
|
|
862
|
+
selected_stratum_index: int
|
|
863
|
+
selected_action_index: int
|
|
864
|
+
if reused_challenger:
|
|
865
|
+
stratum = stratum_by_action.get(
|
|
866
|
+
authoritative_action_sha256
|
|
867
|
+
)
|
|
868
|
+
if stratum is None:
|
|
869
|
+
raise ValueError(
|
|
870
|
+
"forecast challenger is outside forecast audit support"
|
|
871
|
+
)
|
|
872
|
+
selected_stratum_index = tuple(
|
|
873
|
+
value[0] for value in strata
|
|
874
|
+
).index(stratum)
|
|
875
|
+
selected_actions = strata[selected_stratum_index][1]
|
|
876
|
+
selected_action_index = selected_actions.index(
|
|
877
|
+
authoritative_action_sha256
|
|
878
|
+
)
|
|
879
|
+
exploration_action_sha256 = authoritative_action_sha256
|
|
880
|
+
exploration_propensity = 1.0
|
|
881
|
+
else:
|
|
882
|
+
stratum_draw = _stable_unit_interval(
|
|
883
|
+
self.random_seed,
|
|
884
|
+
decision.residual_request_sha256,
|
|
885
|
+
decision.decision_sha256,
|
|
886
|
+
"forecast_stratified_audit_stratum",
|
|
887
|
+
[
|
|
888
|
+
[stratum, list(action_sha256s)]
|
|
889
|
+
for stratum, action_sha256s in strata
|
|
890
|
+
],
|
|
891
|
+
)
|
|
892
|
+
selected_stratum_index = min(
|
|
893
|
+
int(stratum_draw * len(strata)),
|
|
894
|
+
len(strata) - 1,
|
|
895
|
+
)
|
|
896
|
+
selected_actions = strata[selected_stratum_index][1]
|
|
897
|
+
action_draw = _stable_unit_interval(
|
|
898
|
+
self.random_seed,
|
|
899
|
+
decision.residual_request_sha256,
|
|
900
|
+
decision.decision_sha256,
|
|
901
|
+
"forecast_stratified_audit_action",
|
|
902
|
+
strata[selected_stratum_index][0],
|
|
903
|
+
list(selected_actions),
|
|
904
|
+
)
|
|
905
|
+
selected_action_index = min(
|
|
906
|
+
int(action_draw * len(selected_actions)),
|
|
907
|
+
len(selected_actions) - 1,
|
|
908
|
+
)
|
|
909
|
+
exploration_action_sha256 = selected_actions[
|
|
910
|
+
selected_action_index
|
|
911
|
+
]
|
|
912
|
+
exploration_propensity = (
|
|
913
|
+
1.0 / len(strata) / len(selected_actions)
|
|
914
|
+
)
|
|
915
|
+
exploration_stratum = strata[selected_stratum_index][0]
|
|
916
|
+
if exploration_action_sha256 == legacy_action_sha256:
|
|
917
|
+
raise RuntimeError("forecast audit arms unexpectedly coincide")
|
|
918
|
+
authoritative_arm = (
|
|
919
|
+
SamePrefixPairedAuditArm.EXPLORATION
|
|
920
|
+
if reused_challenger
|
|
921
|
+
else SamePrefixPairedAuditArm.LEGACY
|
|
922
|
+
)
|
|
923
|
+
distinct_support = tuple(
|
|
924
|
+
sorted(
|
|
925
|
+
action_sha256
|
|
926
|
+
for _, action_sha256s in strata
|
|
927
|
+
for action_sha256 in action_sha256s
|
|
928
|
+
)
|
|
929
|
+
)
|
|
930
|
+
ranking = evidence["opportunity_ranking"]
|
|
931
|
+
if type(ranking) is not dict:
|
|
932
|
+
raise TypeError("decision lacks an opportunity ranking")
|
|
933
|
+
ranking_sha256 = ranking.get("ranking_sha256")
|
|
934
|
+
if type(ranking_sha256) is not str:
|
|
935
|
+
raise TypeError("opportunity ranking lacks its identity")
|
|
936
|
+
require_sha256(ranking_sha256, "opportunity ranking_sha256")
|
|
937
|
+
return SamePrefixPairedAuditPlan(
|
|
938
|
+
designer_id=self.designer_id,
|
|
939
|
+
designer_version=self.designer_version,
|
|
940
|
+
designer_definition_sha256=self.definition_sha256,
|
|
941
|
+
residual_request_sha256=decision.residual_request_sha256,
|
|
942
|
+
racing_decision_sha256=decision.decision_sha256,
|
|
943
|
+
common_prefix_action_sha256s=(
|
|
944
|
+
decision.prior_selected_action_sha256s
|
|
945
|
+
),
|
|
946
|
+
authoritative_arm=authoritative_arm,
|
|
947
|
+
authoritative_action_sha256=authoritative_action_sha256,
|
|
948
|
+
legacy_action_sha256=legacy_action_sha256,
|
|
949
|
+
exploration_action_sha256=exploration_action_sha256,
|
|
950
|
+
exploration_stratum_key=(
|
|
951
|
+
"forecast_geometry",
|
|
952
|
+
exploration_stratum,
|
|
953
|
+
),
|
|
954
|
+
distinct_exploration_support_action_sha256s=(
|
|
955
|
+
distinct_support
|
|
956
|
+
),
|
|
957
|
+
exploration_selection_propensity=float(
|
|
958
|
+
exploration_propensity
|
|
959
|
+
),
|
|
960
|
+
evidence=freeze_json(
|
|
961
|
+
{
|
|
962
|
+
"adaptive_step": adaptive_step,
|
|
963
|
+
"total_continuation_count": (
|
|
964
|
+
total_continuation_count
|
|
965
|
+
),
|
|
966
|
+
"target_adaptive_step": target_adaptive_step,
|
|
967
|
+
"remaining_authoritative_slots_after_decision": (
|
|
968
|
+
remaining_authoritative_slots_after_decision
|
|
969
|
+
),
|
|
970
|
+
"position_draw_hex": position_draw.hex(),
|
|
971
|
+
"selection_source": selection_source,
|
|
972
|
+
"fallback_decision_sha256": (
|
|
973
|
+
fallback.decision_sha256
|
|
974
|
+
),
|
|
975
|
+
"opportunity_ranking_sha256": ranking_sha256,
|
|
976
|
+
"strata": [
|
|
977
|
+
{
|
|
978
|
+
"stratum": stratum,
|
|
979
|
+
"action_sha256s": list(action_sha256s),
|
|
980
|
+
"conditional_stratum_propensity_hex": (
|
|
981
|
+
(1.0 / len(strata)).hex()
|
|
982
|
+
),
|
|
983
|
+
"conditional_action_propensity_hex": (
|
|
984
|
+
(1.0 / len(action_sha256s)).hex()
|
|
985
|
+
),
|
|
986
|
+
}
|
|
987
|
+
for stratum, action_sha256s in strata
|
|
988
|
+
],
|
|
989
|
+
"stratum_draw_hex": (
|
|
990
|
+
None
|
|
991
|
+
if stratum_draw is None
|
|
992
|
+
else stratum_draw.hex()
|
|
993
|
+
),
|
|
994
|
+
"action_draw_hex": (
|
|
995
|
+
None
|
|
996
|
+
if action_draw is None
|
|
997
|
+
else action_draw.hex()
|
|
998
|
+
),
|
|
999
|
+
"selected_stratum_index": (
|
|
1000
|
+
selected_stratum_index
|
|
1001
|
+
),
|
|
1002
|
+
"selected_action_index": selected_action_index,
|
|
1003
|
+
"challenger_reused": reused_challenger,
|
|
1004
|
+
"authoritative_semantic_role": (
|
|
1005
|
+
"forecast_opportunity_challenger"
|
|
1006
|
+
if reused_challenger
|
|
1007
|
+
else "protected_fallback"
|
|
1008
|
+
),
|
|
1009
|
+
"counterfactual_semantic_role": (
|
|
1010
|
+
"protected_fallback"
|
|
1011
|
+
if reused_challenger
|
|
1012
|
+
else "forecast_stratum_action"
|
|
1013
|
+
),
|
|
1014
|
+
"current_arm_outcomes_observed": False,
|
|
1015
|
+
"plan_must_be_committed_before_arm_evaluation": True,
|
|
1016
|
+
"counterfactual_action_must_be_quarantined": True,
|
|
1017
|
+
"assay_union_may_enter_authoritative_archive": False,
|
|
1018
|
+
}
|
|
1019
|
+
),
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
@dataclass(frozen=True, slots=True)
|
|
1024
|
+
class FactorStratifiedSamePrefixPairedAuditDesigner:
|
|
1025
|
+
"""Choose one distinct exploration arm from a v6 frozen audit support."""
|
|
1026
|
+
|
|
1027
|
+
random_seed: int = 0
|
|
1028
|
+
designer_id: str = SAME_PREFIX_PAIRED_AUDIT_DESIGNER_ID
|
|
1029
|
+
designer_version: int = SAME_PREFIX_PAIRED_AUDIT_DESIGNER_VERSION
|
|
1030
|
+
definition_sha256: str = field(init=False)
|
|
1031
|
+
|
|
1032
|
+
def __post_init__(self) -> None:
|
|
1033
|
+
if type(self.random_seed) is not int or self.random_seed < 0:
|
|
1034
|
+
raise ValueError("random_seed must be non-negative")
|
|
1035
|
+
if self.designer_id != SAME_PREFIX_PAIRED_AUDIT_DESIGNER_ID:
|
|
1036
|
+
raise ValueError("designer_id is immutable")
|
|
1037
|
+
if (
|
|
1038
|
+
self.designer_version
|
|
1039
|
+
!= SAME_PREFIX_PAIRED_AUDIT_DESIGNER_VERSION
|
|
1040
|
+
):
|
|
1041
|
+
raise ValueError("designer_version is immutable")
|
|
1042
|
+
object.__setattr__(
|
|
1043
|
+
self,
|
|
1044
|
+
"definition_sha256",
|
|
1045
|
+
_hash(
|
|
1046
|
+
_DESIGNER_DEFINITION_DOMAIN,
|
|
1047
|
+
{
|
|
1048
|
+
"schema_version": 1,
|
|
1049
|
+
"designer_id": self.designer_id,
|
|
1050
|
+
"designer_version": self.designer_version,
|
|
1051
|
+
"random_seed": self.random_seed,
|
|
1052
|
+
"input": (
|
|
1053
|
+
"authenticated-v6-decision-and-portable-action-market"
|
|
1054
|
+
),
|
|
1055
|
+
"legacy_arm": "frozen-v6-legacy-audit-anchor",
|
|
1056
|
+
"exploration_arm": (
|
|
1057
|
+
"uniform-nonempty-stratum-then-uniform-action-"
|
|
1058
|
+
"conditioned-distinct-from-legacy"
|
|
1059
|
+
),
|
|
1060
|
+
"current_arm_outcomes_observed": False,
|
|
1061
|
+
"assay_union_may_enter_authoritative_archive": False,
|
|
1062
|
+
"workload_objective_model_provider_prompt_config_branches": (
|
|
1063
|
+
False
|
|
1064
|
+
),
|
|
1065
|
+
},
|
|
1066
|
+
),
|
|
1067
|
+
)
|
|
1068
|
+
|
|
1069
|
+
@staticmethod
|
|
1070
|
+
def _read_strata(
|
|
1071
|
+
*,
|
|
1072
|
+
evidence: dict[str, object],
|
|
1073
|
+
action_by_sha256: dict[str, AdaptiveActionDescriptor],
|
|
1074
|
+
prefix: set[str],
|
|
1075
|
+
legacy_action_sha256: str,
|
|
1076
|
+
) -> tuple[tuple[tuple[str, ...], tuple[str, ...]], ...]:
|
|
1077
|
+
raw_strata = evidence.get("audit_strata")
|
|
1078
|
+
if type(raw_strata) is not list or not raw_strata:
|
|
1079
|
+
raise ValueError("v6 decision does not expose audit strata")
|
|
1080
|
+
strata: list[tuple[tuple[str, ...], tuple[str, ...]]] = []
|
|
1081
|
+
seen_actions: set[str] = set()
|
|
1082
|
+
for raw_stratum in raw_strata:
|
|
1083
|
+
if type(raw_stratum) is not dict:
|
|
1084
|
+
raise TypeError("audit stratum must be an exact object")
|
|
1085
|
+
raw_key = raw_stratum.get("stratum_key")
|
|
1086
|
+
raw_actions = raw_stratum.get("action_sha256s")
|
|
1087
|
+
if (
|
|
1088
|
+
type(raw_key) is not list
|
|
1089
|
+
or not raw_key
|
|
1090
|
+
or type(raw_actions) is not list
|
|
1091
|
+
or not raw_actions
|
|
1092
|
+
):
|
|
1093
|
+
raise ValueError("audit stratum is incomplete")
|
|
1094
|
+
key = tuple(raw_key)
|
|
1095
|
+
for value in key:
|
|
1096
|
+
_require_token(value, name="audit stratum level")
|
|
1097
|
+
action_sha256s = tuple(sorted(set(raw_actions)))
|
|
1098
|
+
if len(action_sha256s) != len(raw_actions):
|
|
1099
|
+
raise ValueError("audit stratum repeats an action")
|
|
1100
|
+
for value in action_sha256s:
|
|
1101
|
+
require_sha256(value, "audit stratum action_sha256")
|
|
1102
|
+
if value not in action_by_sha256:
|
|
1103
|
+
raise ValueError(
|
|
1104
|
+
"audit stratum action is outside the frozen market"
|
|
1105
|
+
)
|
|
1106
|
+
if value in prefix:
|
|
1107
|
+
raise ValueError(
|
|
1108
|
+
"audit stratum action is already in the prefix"
|
|
1109
|
+
)
|
|
1110
|
+
if value in seen_actions:
|
|
1111
|
+
raise ValueError(
|
|
1112
|
+
"audit action appears in multiple strata"
|
|
1113
|
+
)
|
|
1114
|
+
seen_actions.add(value)
|
|
1115
|
+
distinct_actions = tuple(
|
|
1116
|
+
value
|
|
1117
|
+
for value in action_sha256s
|
|
1118
|
+
if value != legacy_action_sha256
|
|
1119
|
+
)
|
|
1120
|
+
if distinct_actions:
|
|
1121
|
+
strata.append((key, distinct_actions))
|
|
1122
|
+
if not strata:
|
|
1123
|
+
raise ValueError(
|
|
1124
|
+
"paired audit has no exploration action distinct from legacy"
|
|
1125
|
+
)
|
|
1126
|
+
return tuple(sorted(strata))
|
|
1127
|
+
|
|
1128
|
+
def design(
|
|
1129
|
+
self,
|
|
1130
|
+
*,
|
|
1131
|
+
decision: AdaptiveActionRacingDecision,
|
|
1132
|
+
actions: tuple[AdaptiveActionDescriptor, ...],
|
|
1133
|
+
) -> SamePrefixPairedAuditPlan:
|
|
1134
|
+
"""Freeze two distinct arms without accepting either arm's outcome."""
|
|
1135
|
+
|
|
1136
|
+
self.__post_init__()
|
|
1137
|
+
if type(decision) is not AdaptiveActionRacingDecision:
|
|
1138
|
+
raise TypeError("decision must be exact")
|
|
1139
|
+
decision.__post_init__()
|
|
1140
|
+
if (
|
|
1141
|
+
decision.policy_id != OUTCOME_ADAPTIVE_ACTION_RACING_POLICY_ID
|
|
1142
|
+
or decision.policy_version
|
|
1143
|
+
!= OUTCOME_ADAPTIVE_ACTION_RACING_STRATIFIED_AUDIT_POLICY_VERSION
|
|
1144
|
+
or decision.wave is not AdaptiveActionWave.RANDOMIZED_AUDIT
|
|
1145
|
+
or len(decision.selected_action_sha256s) != 1
|
|
1146
|
+
):
|
|
1147
|
+
raise ValueError(
|
|
1148
|
+
"paired audit requires one final factor-stratified v6 decision"
|
|
1149
|
+
)
|
|
1150
|
+
if type(actions) is not tuple or not actions:
|
|
1151
|
+
raise ValueError("actions must be a non-empty exact tuple")
|
|
1152
|
+
action_by_sha256: dict[str, AdaptiveActionDescriptor] = {}
|
|
1153
|
+
for value in actions:
|
|
1154
|
+
if type(value) is not AdaptiveActionDescriptor:
|
|
1155
|
+
raise TypeError("actions must contain exact descriptors")
|
|
1156
|
+
value.__post_init__()
|
|
1157
|
+
if value.action_sha256 in action_by_sha256:
|
|
1158
|
+
raise ValueError("actions repeat an identity")
|
|
1159
|
+
action_by_sha256[value.action_sha256] = value
|
|
1160
|
+
prefix = set(decision.prior_selected_action_sha256s)
|
|
1161
|
+
if not prefix.issubset(action_by_sha256):
|
|
1162
|
+
raise ValueError("decision prefix is outside the frozen market")
|
|
1163
|
+
evidence = thaw_json(decision.evidence)
|
|
1164
|
+
if (
|
|
1165
|
+
evidence.get("risk_controlled_stratified_audit") is not True
|
|
1166
|
+
or evidence.get("candidate_factor_cells_outcome_blind") is not True
|
|
1167
|
+
):
|
|
1168
|
+
raise ValueError("decision lacks the v6 outcome-blind audit contract")
|
|
1169
|
+
legacy_action_sha256 = evidence.get(
|
|
1170
|
+
"legacy_audit_anchor_action_sha256"
|
|
1171
|
+
)
|
|
1172
|
+
if type(legacy_action_sha256) is not str:
|
|
1173
|
+
raise TypeError("decision lacks a legacy audit anchor")
|
|
1174
|
+
require_sha256(
|
|
1175
|
+
legacy_action_sha256,
|
|
1176
|
+
"legacy_audit_anchor_action_sha256",
|
|
1177
|
+
)
|
|
1178
|
+
if (
|
|
1179
|
+
legacy_action_sha256 not in action_by_sha256
|
|
1180
|
+
or legacy_action_sha256 in prefix
|
|
1181
|
+
):
|
|
1182
|
+
raise ValueError("legacy audit anchor is outside the remaining market")
|
|
1183
|
+
authoritative_action_sha256 = decision.selected_action_sha256s[0]
|
|
1184
|
+
if (
|
|
1185
|
+
authoritative_action_sha256 not in action_by_sha256
|
|
1186
|
+
or authoritative_action_sha256 in prefix
|
|
1187
|
+
):
|
|
1188
|
+
raise ValueError(
|
|
1189
|
+
"authoritative audit action is outside the remaining market"
|
|
1190
|
+
)
|
|
1191
|
+
strata = self._read_strata(
|
|
1192
|
+
evidence=evidence,
|
|
1193
|
+
action_by_sha256=action_by_sha256,
|
|
1194
|
+
prefix=prefix,
|
|
1195
|
+
legacy_action_sha256=legacy_action_sha256,
|
|
1196
|
+
)
|
|
1197
|
+
authoritative_is_distinct_exploration = (
|
|
1198
|
+
evidence.get("audit_exploration_branch") is True
|
|
1199
|
+
and authoritative_action_sha256 != legacy_action_sha256
|
|
1200
|
+
)
|
|
1201
|
+
selected_stratum_index: int | None = None
|
|
1202
|
+
selected_action_index: int | None = None
|
|
1203
|
+
stratum_draw: float | None = None
|
|
1204
|
+
action_draw: float | None = None
|
|
1205
|
+
if authoritative_is_distinct_exploration:
|
|
1206
|
+
for stratum_index, (_, stratum_actions) in enumerate(strata):
|
|
1207
|
+
if authoritative_action_sha256 in stratum_actions:
|
|
1208
|
+
selected_stratum_index = stratum_index
|
|
1209
|
+
selected_action_index = stratum_actions.index(
|
|
1210
|
+
authoritative_action_sha256
|
|
1211
|
+
)
|
|
1212
|
+
break
|
|
1213
|
+
if selected_stratum_index is None:
|
|
1214
|
+
raise ValueError(
|
|
1215
|
+
"selected exploration action is outside distinct support"
|
|
1216
|
+
)
|
|
1217
|
+
else:
|
|
1218
|
+
stratum_draw = _stable_unit_interval(
|
|
1219
|
+
self.random_seed,
|
|
1220
|
+
decision.residual_request_sha256,
|
|
1221
|
+
decision.decision_sha256,
|
|
1222
|
+
"paired_audit_distinct_stratum",
|
|
1223
|
+
[
|
|
1224
|
+
[list(key), list(stratum_actions)]
|
|
1225
|
+
for key, stratum_actions in strata
|
|
1226
|
+
],
|
|
1227
|
+
)
|
|
1228
|
+
selected_stratum_index = min(
|
|
1229
|
+
int(stratum_draw * len(strata)),
|
|
1230
|
+
len(strata) - 1,
|
|
1231
|
+
)
|
|
1232
|
+
selected_key, selected_actions = strata[selected_stratum_index]
|
|
1233
|
+
action_draw = _stable_unit_interval(
|
|
1234
|
+
self.random_seed,
|
|
1235
|
+
decision.residual_request_sha256,
|
|
1236
|
+
decision.decision_sha256,
|
|
1237
|
+
"paired_audit_distinct_action",
|
|
1238
|
+
list(selected_key),
|
|
1239
|
+
list(selected_actions),
|
|
1240
|
+
)
|
|
1241
|
+
selected_action_index = min(
|
|
1242
|
+
int(action_draw * len(selected_actions)),
|
|
1243
|
+
len(selected_actions) - 1,
|
|
1244
|
+
)
|
|
1245
|
+
if selected_stratum_index is None or selected_action_index is None:
|
|
1246
|
+
raise RuntimeError("paired audit did not resolve one exploration arm")
|
|
1247
|
+
exploration_stratum_key, stratum_actions = strata[
|
|
1248
|
+
selected_stratum_index
|
|
1249
|
+
]
|
|
1250
|
+
exploration_action_sha256 = stratum_actions[selected_action_index]
|
|
1251
|
+
exploration_propensity = 1.0 / len(strata) / len(stratum_actions)
|
|
1252
|
+
authoritative_arm = (
|
|
1253
|
+
SamePrefixPairedAuditArm.LEGACY
|
|
1254
|
+
if authoritative_action_sha256 == legacy_action_sha256
|
|
1255
|
+
else SamePrefixPairedAuditArm.EXPLORATION
|
|
1256
|
+
)
|
|
1257
|
+
distinct_support = tuple(
|
|
1258
|
+
sorted(
|
|
1259
|
+
value
|
|
1260
|
+
for _, stratum_actions in strata
|
|
1261
|
+
for value in stratum_actions
|
|
1262
|
+
)
|
|
1263
|
+
)
|
|
1264
|
+
return SamePrefixPairedAuditPlan(
|
|
1265
|
+
designer_id=self.designer_id,
|
|
1266
|
+
designer_version=self.designer_version,
|
|
1267
|
+
designer_definition_sha256=self.definition_sha256,
|
|
1268
|
+
residual_request_sha256=decision.residual_request_sha256,
|
|
1269
|
+
racing_decision_sha256=decision.decision_sha256,
|
|
1270
|
+
common_prefix_action_sha256s=(
|
|
1271
|
+
decision.prior_selected_action_sha256s
|
|
1272
|
+
),
|
|
1273
|
+
authoritative_arm=authoritative_arm,
|
|
1274
|
+
authoritative_action_sha256=authoritative_action_sha256,
|
|
1275
|
+
legacy_action_sha256=legacy_action_sha256,
|
|
1276
|
+
exploration_action_sha256=exploration_action_sha256,
|
|
1277
|
+
exploration_stratum_key=exploration_stratum_key,
|
|
1278
|
+
distinct_exploration_support_action_sha256s=distinct_support,
|
|
1279
|
+
exploration_selection_propensity=float(
|
|
1280
|
+
exploration_propensity
|
|
1281
|
+
),
|
|
1282
|
+
evidence=freeze_json(
|
|
1283
|
+
{
|
|
1284
|
+
"source_racing_decision": decision.to_record(
|
|
1285
|
+
include_evidence=False
|
|
1286
|
+
),
|
|
1287
|
+
"source_audit_exploration_branch": evidence.get(
|
|
1288
|
+
"audit_exploration_branch"
|
|
1289
|
+
),
|
|
1290
|
+
"source_audit_branch_draw_hex": evidence.get(
|
|
1291
|
+
"audit_branch_draw_hex"
|
|
1292
|
+
),
|
|
1293
|
+
"conditioned_distinct_from_legacy": True,
|
|
1294
|
+
"distinct_strata": [
|
|
1295
|
+
{
|
|
1296
|
+
"stratum_key": list(key),
|
|
1297
|
+
"action_sha256s": list(stratum_actions),
|
|
1298
|
+
"conditional_action_propensity_hex": (
|
|
1299
|
+
(1.0 / len(stratum_actions)).hex()
|
|
1300
|
+
),
|
|
1301
|
+
}
|
|
1302
|
+
for key, stratum_actions in strata
|
|
1303
|
+
],
|
|
1304
|
+
"stratum_draw_hex": (
|
|
1305
|
+
None
|
|
1306
|
+
if stratum_draw is None
|
|
1307
|
+
else stratum_draw.hex()
|
|
1308
|
+
),
|
|
1309
|
+
"action_draw_hex": (
|
|
1310
|
+
None
|
|
1311
|
+
if action_draw is None
|
|
1312
|
+
else action_draw.hex()
|
|
1313
|
+
),
|
|
1314
|
+
"selected_stratum_index": selected_stratum_index,
|
|
1315
|
+
"selected_action_index": selected_action_index,
|
|
1316
|
+
"exploration_selection_propensity_hex": (
|
|
1317
|
+
exploration_propensity.hex()
|
|
1318
|
+
),
|
|
1319
|
+
"authoritative_action_reused_when_exploration": (
|
|
1320
|
+
authoritative_is_distinct_exploration
|
|
1321
|
+
),
|
|
1322
|
+
"current_arm_outcomes_observed": False,
|
|
1323
|
+
"plan_must_be_committed_before_arm_evaluation": True,
|
|
1324
|
+
"assay_union_may_enter_authoritative_archive": False,
|
|
1325
|
+
}
|
|
1326
|
+
),
|
|
1327
|
+
)
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
@dataclass(frozen=True, slots=True)
|
|
1331
|
+
class SamePrefixPairedAuditObservation:
|
|
1332
|
+
"""Two independently valued arms joined to one frozen common prefix."""
|
|
1333
|
+
|
|
1334
|
+
plan: SamePrefixPairedAuditPlan
|
|
1335
|
+
legacy_outcome: AdaptiveActionOutcome
|
|
1336
|
+
exploration_outcome: AdaptiveActionOutcome
|
|
1337
|
+
legacy_set_outcome: AdaptiveActionSetOutcome
|
|
1338
|
+
exploration_set_outcome: AdaptiveActionSetOutcome
|
|
1339
|
+
adjudicator_id: str
|
|
1340
|
+
adjudicator_version: int
|
|
1341
|
+
adjudicator_definition_sha256: str
|
|
1342
|
+
observation_sha256: str = field(init=False)
|
|
1343
|
+
|
|
1344
|
+
def __post_init__(self) -> None:
|
|
1345
|
+
if type(self.plan) is not SamePrefixPairedAuditPlan:
|
|
1346
|
+
raise TypeError("plan must be exact")
|
|
1347
|
+
self.plan.__post_init__()
|
|
1348
|
+
for value, name in (
|
|
1349
|
+
(self.legacy_outcome, "legacy_outcome"),
|
|
1350
|
+
(self.exploration_outcome, "exploration_outcome"),
|
|
1351
|
+
):
|
|
1352
|
+
if type(value) is not AdaptiveActionOutcome:
|
|
1353
|
+
raise TypeError(f"{name} must be exact")
|
|
1354
|
+
value.__post_init__()
|
|
1355
|
+
for value, name in (
|
|
1356
|
+
(self.legacy_set_outcome, "legacy_set_outcome"),
|
|
1357
|
+
(self.exploration_set_outcome, "exploration_set_outcome"),
|
|
1358
|
+
):
|
|
1359
|
+
if type(value) is not AdaptiveActionSetOutcome:
|
|
1360
|
+
raise TypeError(f"{name} must be exact")
|
|
1361
|
+
value.__post_init__()
|
|
1362
|
+
if (
|
|
1363
|
+
self.legacy_outcome.action_sha256
|
|
1364
|
+
!= self.plan.legacy_action_sha256
|
|
1365
|
+
or self.exploration_outcome.action_sha256
|
|
1366
|
+
!= self.plan.exploration_action_sha256
|
|
1367
|
+
):
|
|
1368
|
+
raise ValueError("arm outcome does not match the frozen plan")
|
|
1369
|
+
expected_prefix = set(self.plan.common_prefix_action_sha256s)
|
|
1370
|
+
prior_bindings = self.legacy_set_outcome.prior_action_evaluation_bindings
|
|
1371
|
+
if (
|
|
1372
|
+
prior_bindings
|
|
1373
|
+
!= self.exploration_set_outcome.prior_action_evaluation_bindings
|
|
1374
|
+
or {value[0] for value in prior_bindings} != expected_prefix
|
|
1375
|
+
):
|
|
1376
|
+
raise ValueError("paired arms do not share the exact common prefix")
|
|
1377
|
+
if not math.isclose(
|
|
1378
|
+
self.legacy_set_outcome.prior_selected_set_gain,
|
|
1379
|
+
self.exploration_set_outcome.prior_selected_set_gain,
|
|
1380
|
+
rel_tol=1e-12,
|
|
1381
|
+
abs_tol=1e-15,
|
|
1382
|
+
):
|
|
1383
|
+
raise ValueError("paired arms disagree on common-prefix utility")
|
|
1384
|
+
expected_current = (
|
|
1385
|
+
(
|
|
1386
|
+
self.plan.legacy_action_sha256,
|
|
1387
|
+
self.legacy_outcome.evaluation_sha256,
|
|
1388
|
+
),
|
|
1389
|
+
)
|
|
1390
|
+
if self.legacy_set_outcome.current_action_evaluation_bindings != (
|
|
1391
|
+
expected_current
|
|
1392
|
+
):
|
|
1393
|
+
raise ValueError("legacy set outcome does not join its evaluation")
|
|
1394
|
+
expected_current = (
|
|
1395
|
+
(
|
|
1396
|
+
self.plan.exploration_action_sha256,
|
|
1397
|
+
self.exploration_outcome.evaluation_sha256,
|
|
1398
|
+
),
|
|
1399
|
+
)
|
|
1400
|
+
if (
|
|
1401
|
+
self.exploration_set_outcome.current_action_evaluation_bindings
|
|
1402
|
+
!= expected_current
|
|
1403
|
+
):
|
|
1404
|
+
raise ValueError(
|
|
1405
|
+
"exploration set outcome does not join its evaluation"
|
|
1406
|
+
)
|
|
1407
|
+
_require_token(self.adjudicator_id, name="adjudicator_id")
|
|
1408
|
+
if (
|
|
1409
|
+
type(self.adjudicator_version) is not int
|
|
1410
|
+
or self.adjudicator_version <= 0
|
|
1411
|
+
):
|
|
1412
|
+
raise ValueError("adjudicator_version must be positive")
|
|
1413
|
+
require_sha256(
|
|
1414
|
+
self.adjudicator_definition_sha256,
|
|
1415
|
+
"adjudicator_definition_sha256",
|
|
1416
|
+
)
|
|
1417
|
+
object.__setattr__(
|
|
1418
|
+
self,
|
|
1419
|
+
"observation_sha256",
|
|
1420
|
+
_hash(_OBSERVATION_DOMAIN, self._unsigned_record()),
|
|
1421
|
+
)
|
|
1422
|
+
|
|
1423
|
+
@property
|
|
1424
|
+
def conditional_gain_delta(self) -> float:
|
|
1425
|
+
return (
|
|
1426
|
+
self.exploration_set_outcome.conditional_set_gain
|
|
1427
|
+
- self.legacy_set_outcome.conditional_set_gain
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
@property
|
|
1431
|
+
def winner(self) -> SamePrefixPairedAuditWinner:
|
|
1432
|
+
delta = self.conditional_gain_delta
|
|
1433
|
+
if math.isclose(delta, 0.0, rel_tol=1e-12, abs_tol=1e-15):
|
|
1434
|
+
return SamePrefixPairedAuditWinner.TIE
|
|
1435
|
+
if delta > 0.0:
|
|
1436
|
+
return SamePrefixPairedAuditWinner.EXPLORATION
|
|
1437
|
+
return SamePrefixPairedAuditWinner.LEGACY
|
|
1438
|
+
|
|
1439
|
+
@property
|
|
1440
|
+
def authoritative_set_outcome(self) -> AdaptiveActionSetOutcome:
|
|
1441
|
+
if self.plan.authoritative_arm is SamePrefixPairedAuditArm.LEGACY:
|
|
1442
|
+
return self.legacy_set_outcome
|
|
1443
|
+
return self.exploration_set_outcome
|
|
1444
|
+
|
|
1445
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1446
|
+
return {
|
|
1447
|
+
"schema_version": 1,
|
|
1448
|
+
"adjudicator": {
|
|
1449
|
+
"adjudicator_id": self.adjudicator_id,
|
|
1450
|
+
"adjudicator_version": self.adjudicator_version,
|
|
1451
|
+
"definition_sha256": self.adjudicator_definition_sha256,
|
|
1452
|
+
},
|
|
1453
|
+
"plan_sha256": self.plan.plan_sha256,
|
|
1454
|
+
"legacy_outcome_sha256": self.legacy_outcome.outcome_sha256,
|
|
1455
|
+
"exploration_outcome_sha256": (
|
|
1456
|
+
self.exploration_outcome.outcome_sha256
|
|
1457
|
+
),
|
|
1458
|
+
"legacy_set_outcome_sha256": (
|
|
1459
|
+
self.legacy_set_outcome.set_outcome_sha256
|
|
1460
|
+
),
|
|
1461
|
+
"exploration_set_outcome_sha256": (
|
|
1462
|
+
self.exploration_set_outcome.set_outcome_sha256
|
|
1463
|
+
),
|
|
1464
|
+
"common_prefix_selected_set_gain_hex": (
|
|
1465
|
+
self.legacy_set_outcome.prior_selected_set_gain.hex()
|
|
1466
|
+
),
|
|
1467
|
+
"legacy_conditional_gain_hex": (
|
|
1468
|
+
self.legacy_set_outcome.conditional_set_gain.hex()
|
|
1469
|
+
),
|
|
1470
|
+
"exploration_conditional_gain_hex": (
|
|
1471
|
+
self.exploration_set_outcome.conditional_set_gain.hex()
|
|
1472
|
+
),
|
|
1473
|
+
"conditional_gain_delta_hex": (
|
|
1474
|
+
self.conditional_gain_delta.hex()
|
|
1475
|
+
),
|
|
1476
|
+
"winner": self.winner.value,
|
|
1477
|
+
"authoritative_arm": self.plan.authoritative_arm.value,
|
|
1478
|
+
"authoritative_set_outcome_sha256": (
|
|
1479
|
+
self.authoritative_set_outcome.set_outcome_sha256
|
|
1480
|
+
),
|
|
1481
|
+
"assay_union_admitted_to_authoritative_archive": False,
|
|
1482
|
+
"counterfactual_endpoints_share_one_prefix": True,
|
|
1483
|
+
"workload_objective_model_provider_prompt_config_branches": False,
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
def to_record(self, *, include_evidence: bool = False) -> dict[str, object]:
|
|
1487
|
+
self.__post_init__()
|
|
1488
|
+
return {
|
|
1489
|
+
**self._unsigned_record(),
|
|
1490
|
+
"plan": self.plan.to_record(include_evidence=include_evidence),
|
|
1491
|
+
"legacy_outcome": self.legacy_outcome.to_record(),
|
|
1492
|
+
"exploration_outcome": self.exploration_outcome.to_record(),
|
|
1493
|
+
"legacy_set_outcome": self.legacy_set_outcome.to_record(),
|
|
1494
|
+
"exploration_set_outcome": (
|
|
1495
|
+
self.exploration_set_outcome.to_record()
|
|
1496
|
+
),
|
|
1497
|
+
"observation_sha256": self.observation_sha256,
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
@dataclass(frozen=True, slots=True)
|
|
1502
|
+
class SamePrefixPairedAuditAdjudicator:
|
|
1503
|
+
"""Build one workload-opaque observation from real arm outcomes."""
|
|
1504
|
+
|
|
1505
|
+
adjudicator_id: str = SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_ID
|
|
1506
|
+
adjudicator_version: int = SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_VERSION
|
|
1507
|
+
definition_sha256: str = field(init=False)
|
|
1508
|
+
|
|
1509
|
+
def __post_init__(self) -> None:
|
|
1510
|
+
if self.adjudicator_id != SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_ID:
|
|
1511
|
+
raise ValueError("adjudicator_id is immutable")
|
|
1512
|
+
if (
|
|
1513
|
+
self.adjudicator_version
|
|
1514
|
+
!= SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_VERSION
|
|
1515
|
+
):
|
|
1516
|
+
raise ValueError("adjudicator_version is immutable")
|
|
1517
|
+
object.__setattr__(
|
|
1518
|
+
self,
|
|
1519
|
+
"definition_sha256",
|
|
1520
|
+
_hash(
|
|
1521
|
+
_ADJUDICATOR_DEFINITION_DOMAIN,
|
|
1522
|
+
{
|
|
1523
|
+
"schema_version": 1,
|
|
1524
|
+
"adjudicator_id": self.adjudicator_id,
|
|
1525
|
+
"adjudicator_version": self.adjudicator_version,
|
|
1526
|
+
"comparison": (
|
|
1527
|
+
"conditional-set-gain-at-identical-prior-bindings"
|
|
1528
|
+
),
|
|
1529
|
+
"assay_union_admitted_to_authoritative_archive": False,
|
|
1530
|
+
"workload_objective_model_provider_prompt_config_branches": (
|
|
1531
|
+
False
|
|
1532
|
+
),
|
|
1533
|
+
},
|
|
1534
|
+
),
|
|
1535
|
+
)
|
|
1536
|
+
|
|
1537
|
+
def adjudicate(
|
|
1538
|
+
self,
|
|
1539
|
+
*,
|
|
1540
|
+
plan: SamePrefixPairedAuditPlan,
|
|
1541
|
+
legacy_outcome: AdaptiveActionOutcome,
|
|
1542
|
+
exploration_outcome: AdaptiveActionOutcome,
|
|
1543
|
+
legacy_set_outcome: AdaptiveActionSetOutcome,
|
|
1544
|
+
exploration_set_outcome: AdaptiveActionSetOutcome,
|
|
1545
|
+
) -> SamePrefixPairedAuditObservation:
|
|
1546
|
+
self.__post_init__()
|
|
1547
|
+
return SamePrefixPairedAuditObservation(
|
|
1548
|
+
plan=plan,
|
|
1549
|
+
legacy_outcome=legacy_outcome,
|
|
1550
|
+
exploration_outcome=exploration_outcome,
|
|
1551
|
+
legacy_set_outcome=legacy_set_outcome,
|
|
1552
|
+
exploration_set_outcome=exploration_set_outcome,
|
|
1553
|
+
adjudicator_id=self.adjudicator_id,
|
|
1554
|
+
adjudicator_version=self.adjudicator_version,
|
|
1555
|
+
adjudicator_definition_sha256=self.definition_sha256,
|
|
1556
|
+
)
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
__all__ = [
|
|
1560
|
+
"FactorStratifiedSamePrefixPairedAuditDesigner",
|
|
1561
|
+
"FORECAST_OPPORTUNITY_SAME_PREFIX_AUDIT_DESIGNER_IDS",
|
|
1562
|
+
"FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_ID",
|
|
1563
|
+
"FORECAST_OPPORTUNITY_SAME_PREFIX_SHADOW_DESIGNER_VERSION",
|
|
1564
|
+
"FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_ID",
|
|
1565
|
+
"FORECAST_STRATIFIED_SAME_PREFIX_AUDIT_DESIGNER_VERSION",
|
|
1566
|
+
"ForecastOpportunitySamePrefixAuditDesignerPort",
|
|
1567
|
+
"ForecastOpportunitySamePrefixShadowDesigner",
|
|
1568
|
+
"ForecastOpportunitySamePrefixShadowDesignerPort",
|
|
1569
|
+
"ForecastStratifiedSamePrefixAuditDesigner",
|
|
1570
|
+
"SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_ID",
|
|
1571
|
+
"SAME_PREFIX_PAIRED_AUDIT_ADJUDICATOR_VERSION",
|
|
1572
|
+
"SAME_PREFIX_PAIRED_AUDIT_DESIGNER_ID",
|
|
1573
|
+
"SAME_PREFIX_PAIRED_AUDIT_DESIGNER_VERSION",
|
|
1574
|
+
"SamePrefixPairedAuditAdjudicator",
|
|
1575
|
+
"SamePrefixPairedAuditArm",
|
|
1576
|
+
"SamePrefixPairedAuditDesignerPort",
|
|
1577
|
+
"SamePrefixPairedAuditObservation",
|
|
1578
|
+
"SamePrefixPairedAuditPlan",
|
|
1579
|
+
"SamePrefixPairedAuditWinner",
|
|
1580
|
+
]
|