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,1154 @@
|
|
|
1
|
+
"""Prequential calibration for current-prefix archive opportunity.
|
|
2
|
+
|
|
3
|
+
The raw language-model forecast remains useful semantic evidence, but it is
|
|
4
|
+
not assumed to be a calibrated numerical distribution. This module exposes a
|
|
5
|
+
small application port that converts a raw conditional archive-opportunity
|
|
6
|
+
forecast into a prior-only predictive distribution and an abstention decision.
|
|
7
|
+
|
|
8
|
+
All features are workload opaque: evolutionary stage, model/expert lane,
|
|
9
|
+
typed operator, native rank, prior score, forecast reliability, and observed
|
|
10
|
+
prefix scale. Objective names, workload identifiers, simulator fields,
|
|
11
|
+
provider names, and current eligible-candidate outcomes are absent.
|
|
12
|
+
|
|
13
|
+
The default empirical implementation uses:
|
|
14
|
+
|
|
15
|
+
* archive-prefix normalization for cross-workload scale transport;
|
|
16
|
+
* log residuals for multiplicative forecast error;
|
|
17
|
+
* a hierarchy of stage/lane/operator calibration cells;
|
|
18
|
+
* a prequential Spearman skill gate;
|
|
19
|
+
* support-distance abstention;
|
|
20
|
+
* a finite-sample residual lower quantile; and
|
|
21
|
+
* a two-part lower expected-opportunity bound that separates probability of a
|
|
22
|
+
positive contribution from its positive magnitude.
|
|
23
|
+
|
|
24
|
+
This is recommendation evidence only. The protected incumbent remains the
|
|
25
|
+
selection authority whenever the calibrator abstains.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import hashlib
|
|
31
|
+
import json
|
|
32
|
+
import math
|
|
33
|
+
import re
|
|
34
|
+
from dataclasses import dataclass, field
|
|
35
|
+
from enum import Enum
|
|
36
|
+
from statistics import fmean
|
|
37
|
+
from typing import Protocol, runtime_checkable
|
|
38
|
+
|
|
39
|
+
from agent_evolve.domain.patch import require_sha256
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID = (
|
|
43
|
+
"hierarchical_prequential_archive_opportunity"
|
|
44
|
+
)
|
|
45
|
+
PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION = 1
|
|
46
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
47
|
+
_OBSERVATION_DOMAIN = (
|
|
48
|
+
b"agent-evolve:archive-opportunity-calibration-observation:v1\x00"
|
|
49
|
+
)
|
|
50
|
+
_CONTEXT_DOMAIN = (
|
|
51
|
+
b"agent-evolve:archive-opportunity-calibration-context:v1\x00"
|
|
52
|
+
)
|
|
53
|
+
_RESULT_DOMAIN = (
|
|
54
|
+
b"agent-evolve:archive-opportunity-calibration-result:v1\x00"
|
|
55
|
+
)
|
|
56
|
+
_SNAPSHOT_DOMAIN = (
|
|
57
|
+
b"agent-evolve:archive-opportunity-calibration-snapshot:v1\x00"
|
|
58
|
+
)
|
|
59
|
+
_DEFINITION_DOMAIN = (
|
|
60
|
+
b"agent-evolve:archive-opportunity-calibration-policy:v1\x00"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _canonical_json(value: object) -> bytes:
|
|
65
|
+
return json.dumps(
|
|
66
|
+
value,
|
|
67
|
+
allow_nan=False,
|
|
68
|
+
ensure_ascii=True,
|
|
69
|
+
separators=(",", ":"),
|
|
70
|
+
sort_keys=True,
|
|
71
|
+
).encode("ascii", errors="strict")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
75
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _require_token(value: str, *, name: str) -> None:
|
|
79
|
+
if type(value) is not str or _TOKEN.fullmatch(value) is None:
|
|
80
|
+
raise ValueError(f"{name} must use the closed token grammar")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _require_probability(value: float, *, name: str) -> None:
|
|
84
|
+
if (
|
|
85
|
+
type(value) is not float
|
|
86
|
+
or not math.isfinite(value)
|
|
87
|
+
or not 0.0 <= value <= 1.0
|
|
88
|
+
):
|
|
89
|
+
raise ValueError(f"{name} must lie in [0, 1]")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _require_nonnegative(value: float, *, name: str) -> None:
|
|
93
|
+
if (
|
|
94
|
+
type(value) is not float
|
|
95
|
+
or not math.isfinite(value)
|
|
96
|
+
or value < 0.0
|
|
97
|
+
):
|
|
98
|
+
raise ValueError(f"{name} must be finite and non-negative")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _nearest_rank_quantile(
|
|
102
|
+
values: tuple[float, ...],
|
|
103
|
+
probability: float,
|
|
104
|
+
) -> float:
|
|
105
|
+
"""Return the deterministic finite-sample nearest-rank quantile."""
|
|
106
|
+
|
|
107
|
+
if not values:
|
|
108
|
+
raise ValueError("a quantile requires at least one observation")
|
|
109
|
+
_require_probability(probability, name="probability")
|
|
110
|
+
if any(type(value) is not float or not math.isfinite(value) for value in values):
|
|
111
|
+
raise ValueError("quantile values must be finite exact floats")
|
|
112
|
+
ordered = tuple(sorted(values))
|
|
113
|
+
rank = max(1, math.ceil(probability * len(ordered)))
|
|
114
|
+
return ordered[min(len(ordered), rank) - 1]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _rank(values: tuple[float, ...]) -> tuple[float, ...]:
|
|
118
|
+
order = sorted(range(len(values)), key=lambda index: values[index])
|
|
119
|
+
result = [0.0] * len(values)
|
|
120
|
+
cursor = 0
|
|
121
|
+
while cursor < len(order):
|
|
122
|
+
end = cursor + 1
|
|
123
|
+
while (
|
|
124
|
+
end < len(order)
|
|
125
|
+
and values[order[end]] == values[order[cursor]]
|
|
126
|
+
):
|
|
127
|
+
end += 1
|
|
128
|
+
average = (cursor + 1 + end) / 2.0
|
|
129
|
+
for position in range(cursor, end):
|
|
130
|
+
result[order[position]] = average
|
|
131
|
+
cursor = end
|
|
132
|
+
return tuple(result)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _pearson(
|
|
136
|
+
left: tuple[float, ...],
|
|
137
|
+
right: tuple[float, ...],
|
|
138
|
+
) -> float | None:
|
|
139
|
+
if len(left) != len(right) or len(left) < 2:
|
|
140
|
+
return None
|
|
141
|
+
left_mean = fmean(left)
|
|
142
|
+
right_mean = fmean(right)
|
|
143
|
+
numerator = math.fsum(
|
|
144
|
+
(x - left_mean) * (y - right_mean)
|
|
145
|
+
for x, y in zip(left, right, strict=True)
|
|
146
|
+
)
|
|
147
|
+
left_norm = math.sqrt(
|
|
148
|
+
math.fsum((value - left_mean) ** 2 for value in left)
|
|
149
|
+
)
|
|
150
|
+
right_norm = math.sqrt(
|
|
151
|
+
math.fsum((value - right_mean) ** 2 for value in right)
|
|
152
|
+
)
|
|
153
|
+
if left_norm == 0.0 or right_norm == 0.0:
|
|
154
|
+
return None
|
|
155
|
+
return float(numerator / (left_norm * right_norm))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _spearman(
|
|
159
|
+
left: tuple[float, ...],
|
|
160
|
+
right: tuple[float, ...],
|
|
161
|
+
) -> float | None:
|
|
162
|
+
return _pearson(_rank(left), _rank(right))
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _wilson_lower_bound(
|
|
166
|
+
*,
|
|
167
|
+
positive_count: int,
|
|
168
|
+
observation_count: int,
|
|
169
|
+
z_value: float,
|
|
170
|
+
) -> float:
|
|
171
|
+
if (
|
|
172
|
+
type(positive_count) is not int
|
|
173
|
+
or type(observation_count) is not int
|
|
174
|
+
or not 0 <= positive_count <= observation_count
|
|
175
|
+
or observation_count <= 0
|
|
176
|
+
):
|
|
177
|
+
raise ValueError("Wilson counts are invalid")
|
|
178
|
+
if type(z_value) is not float or not math.isfinite(z_value) or z_value <= 0.0:
|
|
179
|
+
raise ValueError("z_value must be a positive finite float")
|
|
180
|
+
probability = positive_count / observation_count
|
|
181
|
+
z_squared = z_value * z_value
|
|
182
|
+
denominator = 1.0 + z_squared / observation_count
|
|
183
|
+
centre = probability + z_squared / (2.0 * observation_count)
|
|
184
|
+
radius = z_value * math.sqrt(
|
|
185
|
+
(
|
|
186
|
+
probability * (1.0 - probability)
|
|
187
|
+
+ z_squared / (4.0 * observation_count)
|
|
188
|
+
)
|
|
189
|
+
/ observation_count
|
|
190
|
+
)
|
|
191
|
+
return float(max(0.0, (centre - radius) / denominator))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass(frozen=True, slots=True)
|
|
195
|
+
class ArchiveOpportunityActionContext:
|
|
196
|
+
"""Outcome-blind generic context for one candidate opportunity."""
|
|
197
|
+
|
|
198
|
+
action_sha256: str
|
|
199
|
+
decision_index: int
|
|
200
|
+
lane_id: str
|
|
201
|
+
operator_id: str
|
|
202
|
+
native_rank: int
|
|
203
|
+
lane_size: int
|
|
204
|
+
prior_score: float
|
|
205
|
+
parent_generated_in_current_run: bool
|
|
206
|
+
context_sha256: str = field(init=False)
|
|
207
|
+
|
|
208
|
+
def __post_init__(self) -> None:
|
|
209
|
+
require_sha256(self.action_sha256, "action_sha256")
|
|
210
|
+
if type(self.decision_index) is not int or self.decision_index <= 0:
|
|
211
|
+
raise ValueError("decision_index must be positive")
|
|
212
|
+
_require_token(self.lane_id, name="lane_id")
|
|
213
|
+
_require_token(self.operator_id, name="operator_id")
|
|
214
|
+
if (
|
|
215
|
+
type(self.native_rank) is not int
|
|
216
|
+
or type(self.lane_size) is not int
|
|
217
|
+
or self.native_rank <= 0
|
|
218
|
+
or self.lane_size <= 0
|
|
219
|
+
or self.native_rank > self.lane_size
|
|
220
|
+
):
|
|
221
|
+
raise ValueError("native rank must fit the positive lane size")
|
|
222
|
+
_require_probability(self.prior_score, name="prior_score")
|
|
223
|
+
if type(self.parent_generated_in_current_run) is not bool:
|
|
224
|
+
raise TypeError(
|
|
225
|
+
"parent_generated_in_current_run must be exact"
|
|
226
|
+
)
|
|
227
|
+
object.__setattr__(
|
|
228
|
+
self,
|
|
229
|
+
"context_sha256",
|
|
230
|
+
_hash(_CONTEXT_DOMAIN, self._unsigned_record()),
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def rank_quality(self) -> float:
|
|
235
|
+
if self.lane_size == 1:
|
|
236
|
+
return 1.0
|
|
237
|
+
return 1.0 - (
|
|
238
|
+
(self.native_rank - 1) / float(self.lane_size - 1)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
242
|
+
return {
|
|
243
|
+
"schema_version": 1,
|
|
244
|
+
"action_sha256": self.action_sha256,
|
|
245
|
+
"decision_index": self.decision_index,
|
|
246
|
+
"lane_id": self.lane_id,
|
|
247
|
+
"operator_id": self.operator_id,
|
|
248
|
+
"native_rank": self.native_rank,
|
|
249
|
+
"lane_size": self.lane_size,
|
|
250
|
+
"rank_quality_hex": self.rank_quality.hex(),
|
|
251
|
+
"prior_score_hex": self.prior_score.hex(),
|
|
252
|
+
"parent_generated_in_current_run": (
|
|
253
|
+
self.parent_generated_in_current_run
|
|
254
|
+
),
|
|
255
|
+
"workload_objective_provider_prompt_fields_present": False,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
def to_record(self) -> dict[str, object]:
|
|
259
|
+
self.__post_init__()
|
|
260
|
+
return {
|
|
261
|
+
**self._unsigned_record(),
|
|
262
|
+
"context_sha256": self.context_sha256,
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclass(frozen=True, slots=True)
|
|
267
|
+
class ArchiveOpportunityCalibrationRequest:
|
|
268
|
+
"""One raw current-prefix forecast submitted for calibration."""
|
|
269
|
+
|
|
270
|
+
context: ArchiveOpportunityActionContext
|
|
271
|
+
forecast_reliability: float
|
|
272
|
+
raw_adverse_gain: float
|
|
273
|
+
raw_central_gain: float
|
|
274
|
+
raw_favorable_gain: float
|
|
275
|
+
raw_acquisition_value: float
|
|
276
|
+
prefix_gain: float
|
|
277
|
+
prefix_action_count: int
|
|
278
|
+
|
|
279
|
+
def __post_init__(self) -> None:
|
|
280
|
+
if type(self.context) is not ArchiveOpportunityActionContext:
|
|
281
|
+
raise TypeError("context must be exact")
|
|
282
|
+
self.context.__post_init__()
|
|
283
|
+
_require_probability(
|
|
284
|
+
self.forecast_reliability,
|
|
285
|
+
name="forecast_reliability",
|
|
286
|
+
)
|
|
287
|
+
for name in (
|
|
288
|
+
"raw_adverse_gain",
|
|
289
|
+
"raw_central_gain",
|
|
290
|
+
"raw_favorable_gain",
|
|
291
|
+
"raw_acquisition_value",
|
|
292
|
+
"prefix_gain",
|
|
293
|
+
):
|
|
294
|
+
_require_nonnegative(getattr(self, name), name=name)
|
|
295
|
+
if (
|
|
296
|
+
type(self.prefix_action_count) is not int
|
|
297
|
+
or self.prefix_action_count <= 0
|
|
298
|
+
):
|
|
299
|
+
raise ValueError("prefix_action_count must be positive")
|
|
300
|
+
if self.raw_adverse_gain > self.raw_favorable_gain:
|
|
301
|
+
raise ValueError("adverse gain cannot exceed favorable gain")
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def prefix_mean_gain(self) -> float:
|
|
305
|
+
return self.prefix_gain / self.prefix_action_count
|
|
306
|
+
|
|
307
|
+
def to_record(self) -> dict[str, object]:
|
|
308
|
+
self.__post_init__()
|
|
309
|
+
return {
|
|
310
|
+
"schema_version": 1,
|
|
311
|
+
"context": self.context.to_record(),
|
|
312
|
+
"forecast_reliability_hex": self.forecast_reliability.hex(),
|
|
313
|
+
"raw_adverse_gain_hex": self.raw_adverse_gain.hex(),
|
|
314
|
+
"raw_central_gain_hex": self.raw_central_gain.hex(),
|
|
315
|
+
"raw_favorable_gain_hex": self.raw_favorable_gain.hex(),
|
|
316
|
+
"raw_acquisition_value_hex": (
|
|
317
|
+
self.raw_acquisition_value.hex()
|
|
318
|
+
),
|
|
319
|
+
"prefix_gain_hex": self.prefix_gain.hex(),
|
|
320
|
+
"prefix_action_count": self.prefix_action_count,
|
|
321
|
+
"prefix_mean_gain_hex": self.prefix_mean_gain.hex(),
|
|
322
|
+
"eligible_candidate_outcomes_observed": False,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class ArchiveOpportunityCalibrationEvidenceRole(str, Enum):
|
|
327
|
+
"""How the forecast target entered the authenticated evidence stream."""
|
|
328
|
+
|
|
329
|
+
AUTHORITATIVE_SELECTED = "authoritative_selected"
|
|
330
|
+
SAME_PREFIX_PAIRED_AUTHORITATIVE = (
|
|
331
|
+
"same_prefix_paired_authoritative"
|
|
332
|
+
)
|
|
333
|
+
SAME_PREFIX_PAIRED_COUNTERFACTUAL = (
|
|
334
|
+
"same_prefix_paired_counterfactual"
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@dataclass(frozen=True, slots=True)
|
|
339
|
+
class ArchiveOpportunityCalibrationObservation:
|
|
340
|
+
"""One forecast joined to a later exact conditional-gain observation."""
|
|
341
|
+
|
|
342
|
+
request: ArchiveOpportunityCalibrationRequest
|
|
343
|
+
realized_conditional_gain: float
|
|
344
|
+
decision_sha256: str
|
|
345
|
+
outcome_sha256: str
|
|
346
|
+
evidence_cutoff_ordinal: int
|
|
347
|
+
evidence_role: ArchiveOpportunityCalibrationEvidenceRole = (
|
|
348
|
+
ArchiveOpportunityCalibrationEvidenceRole.AUTHORITATIVE_SELECTED
|
|
349
|
+
)
|
|
350
|
+
sampling_propensity: float = 1.0
|
|
351
|
+
paired_observation_sha256: str | None = None
|
|
352
|
+
observation_sha256: str = field(init=False)
|
|
353
|
+
|
|
354
|
+
def __post_init__(self) -> None:
|
|
355
|
+
if type(self.request) is not ArchiveOpportunityCalibrationRequest:
|
|
356
|
+
raise TypeError("request must be exact")
|
|
357
|
+
self.request.__post_init__()
|
|
358
|
+
_require_nonnegative(
|
|
359
|
+
self.realized_conditional_gain,
|
|
360
|
+
name="realized_conditional_gain",
|
|
361
|
+
)
|
|
362
|
+
require_sha256(self.decision_sha256, "decision_sha256")
|
|
363
|
+
require_sha256(self.outcome_sha256, "outcome_sha256")
|
|
364
|
+
if (
|
|
365
|
+
type(self.evidence_cutoff_ordinal) is not int
|
|
366
|
+
or self.evidence_cutoff_ordinal <= 0
|
|
367
|
+
):
|
|
368
|
+
raise ValueError("evidence_cutoff_ordinal must be positive")
|
|
369
|
+
if (
|
|
370
|
+
type(self.evidence_role)
|
|
371
|
+
is not ArchiveOpportunityCalibrationEvidenceRole
|
|
372
|
+
):
|
|
373
|
+
raise TypeError("evidence_role must be exact")
|
|
374
|
+
if (
|
|
375
|
+
type(self.sampling_propensity) is not float
|
|
376
|
+
or not math.isfinite(self.sampling_propensity)
|
|
377
|
+
or not 0.0 < self.sampling_propensity <= 1.0
|
|
378
|
+
):
|
|
379
|
+
raise ValueError(
|
|
380
|
+
"sampling_propensity must be a finite positive probability"
|
|
381
|
+
)
|
|
382
|
+
if self.paired_observation_sha256 is not None:
|
|
383
|
+
require_sha256(
|
|
384
|
+
self.paired_observation_sha256,
|
|
385
|
+
"paired_observation_sha256",
|
|
386
|
+
)
|
|
387
|
+
is_legacy_schema = (
|
|
388
|
+
self.evidence_role
|
|
389
|
+
is (
|
|
390
|
+
ArchiveOpportunityCalibrationEvidenceRole
|
|
391
|
+
.AUTHORITATIVE_SELECTED
|
|
392
|
+
)
|
|
393
|
+
and self.sampling_propensity == 1.0
|
|
394
|
+
and self.paired_observation_sha256 is None
|
|
395
|
+
)
|
|
396
|
+
if (
|
|
397
|
+
not is_legacy_schema
|
|
398
|
+
and self.paired_observation_sha256 is None
|
|
399
|
+
):
|
|
400
|
+
raise ValueError(
|
|
401
|
+
"paired calibration evidence requires its observation identity"
|
|
402
|
+
)
|
|
403
|
+
object.__setattr__(
|
|
404
|
+
self,
|
|
405
|
+
"observation_sha256",
|
|
406
|
+
_hash(_OBSERVATION_DOMAIN, self._unsigned_record()),
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
@property
|
|
410
|
+
def positive(self) -> bool:
|
|
411
|
+
return self.realized_conditional_gain > 0.0
|
|
412
|
+
|
|
413
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
414
|
+
result: dict[str, object] = {
|
|
415
|
+
"schema_version": 1,
|
|
416
|
+
"request": self.request.to_record(),
|
|
417
|
+
"realized_conditional_gain_hex": (
|
|
418
|
+
self.realized_conditional_gain.hex()
|
|
419
|
+
),
|
|
420
|
+
"positive": self.positive,
|
|
421
|
+
"decision_sha256": self.decision_sha256,
|
|
422
|
+
"outcome_sha256": self.outcome_sha256,
|
|
423
|
+
"evidence_cutoff_ordinal": self.evidence_cutoff_ordinal,
|
|
424
|
+
"current_or_future_candidate_outcomes_used": False,
|
|
425
|
+
}
|
|
426
|
+
if (
|
|
427
|
+
self.evidence_role
|
|
428
|
+
is not (
|
|
429
|
+
ArchiveOpportunityCalibrationEvidenceRole
|
|
430
|
+
.AUTHORITATIVE_SELECTED
|
|
431
|
+
)
|
|
432
|
+
or self.sampling_propensity != 1.0
|
|
433
|
+
or self.paired_observation_sha256 is not None
|
|
434
|
+
):
|
|
435
|
+
result.update(
|
|
436
|
+
{
|
|
437
|
+
"schema_version": 2,
|
|
438
|
+
"evidence_role": self.evidence_role.value,
|
|
439
|
+
"sampling_propensity_hex": (
|
|
440
|
+
self.sampling_propensity.hex()
|
|
441
|
+
),
|
|
442
|
+
"paired_observation_sha256": (
|
|
443
|
+
self.paired_observation_sha256
|
|
444
|
+
),
|
|
445
|
+
"same_prefix_counterfactual_is_not_archive_"
|
|
446
|
+
"publication": (
|
|
447
|
+
self.evidence_role
|
|
448
|
+
is (
|
|
449
|
+
ArchiveOpportunityCalibrationEvidenceRole
|
|
450
|
+
.SAME_PREFIX_PAIRED_COUNTERFACTUAL
|
|
451
|
+
)
|
|
452
|
+
),
|
|
453
|
+
}
|
|
454
|
+
)
|
|
455
|
+
return result
|
|
456
|
+
|
|
457
|
+
def to_record(self) -> dict[str, object]:
|
|
458
|
+
self.__post_init__()
|
|
459
|
+
return {
|
|
460
|
+
**self._unsigned_record(),
|
|
461
|
+
"observation_sha256": self.observation_sha256,
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
@dataclass(frozen=True, slots=True)
|
|
466
|
+
class ArchiveOpportunityCalibrationResult:
|
|
467
|
+
"""Authenticated calibrated opportunity and abstention evidence."""
|
|
468
|
+
|
|
469
|
+
action_sha256: str
|
|
470
|
+
calibration_id: str
|
|
471
|
+
calibration_version: int
|
|
472
|
+
calibration_definition_sha256: str
|
|
473
|
+
observation_snapshot_sha256: str
|
|
474
|
+
selected_stratum: str
|
|
475
|
+
stratum_support_count: int
|
|
476
|
+
skill_support_count: int
|
|
477
|
+
prequential_rank_skill: float | None
|
|
478
|
+
raw_normalized_acquisition: float
|
|
479
|
+
minimum_supported_normalized_acquisition: float | None
|
|
480
|
+
maximum_supported_normalized_acquisition: float | None
|
|
481
|
+
support_log_distance: float
|
|
482
|
+
positive_count: int
|
|
483
|
+
posterior_positive_probability: float
|
|
484
|
+
lower_positive_probability: float
|
|
485
|
+
conformal_lower_gain: float
|
|
486
|
+
lower_expected_gain: float
|
|
487
|
+
calibrated_acquisition_value: float
|
|
488
|
+
calibrated_upper_gain: float
|
|
489
|
+
abstained: bool
|
|
490
|
+
abstention_reason: str | None
|
|
491
|
+
result_sha256: str = field(init=False)
|
|
492
|
+
|
|
493
|
+
def __post_init__(self) -> None:
|
|
494
|
+
require_sha256(self.action_sha256, "action_sha256")
|
|
495
|
+
_require_token(self.calibration_id, name="calibration_id")
|
|
496
|
+
if (
|
|
497
|
+
type(self.calibration_version) is not int
|
|
498
|
+
or self.calibration_version <= 0
|
|
499
|
+
):
|
|
500
|
+
raise ValueError("calibration_version must be positive")
|
|
501
|
+
require_sha256(
|
|
502
|
+
self.calibration_definition_sha256,
|
|
503
|
+
"calibration_definition_sha256",
|
|
504
|
+
)
|
|
505
|
+
require_sha256(
|
|
506
|
+
self.observation_snapshot_sha256,
|
|
507
|
+
"observation_snapshot_sha256",
|
|
508
|
+
)
|
|
509
|
+
_require_token(self.selected_stratum, name="selected_stratum")
|
|
510
|
+
for name in (
|
|
511
|
+
"stratum_support_count",
|
|
512
|
+
"skill_support_count",
|
|
513
|
+
"positive_count",
|
|
514
|
+
):
|
|
515
|
+
value = getattr(self, name)
|
|
516
|
+
if type(value) is not int or value < 0:
|
|
517
|
+
raise ValueError(f"{name} must be non-negative")
|
|
518
|
+
if self.positive_count > self.stratum_support_count:
|
|
519
|
+
raise ValueError("positive_count exceeds stratum support")
|
|
520
|
+
if self.prequential_rank_skill is not None and (
|
|
521
|
+
type(self.prequential_rank_skill) is not float
|
|
522
|
+
or not math.isfinite(self.prequential_rank_skill)
|
|
523
|
+
or not -1.0 <= self.prequential_rank_skill <= 1.0
|
|
524
|
+
):
|
|
525
|
+
raise ValueError("prequential_rank_skill must lie in [-1,1]")
|
|
526
|
+
for name in (
|
|
527
|
+
"raw_normalized_acquisition",
|
|
528
|
+
"support_log_distance",
|
|
529
|
+
"conformal_lower_gain",
|
|
530
|
+
"lower_expected_gain",
|
|
531
|
+
"calibrated_acquisition_value",
|
|
532
|
+
"calibrated_upper_gain",
|
|
533
|
+
):
|
|
534
|
+
_require_nonnegative(getattr(self, name), name=name)
|
|
535
|
+
for name in (
|
|
536
|
+
"minimum_supported_normalized_acquisition",
|
|
537
|
+
"maximum_supported_normalized_acquisition",
|
|
538
|
+
):
|
|
539
|
+
value = getattr(self, name)
|
|
540
|
+
if value is not None:
|
|
541
|
+
_require_nonnegative(value, name=name)
|
|
542
|
+
if (
|
|
543
|
+
self.minimum_supported_normalized_acquisition is None
|
|
544
|
+
) != (
|
|
545
|
+
self.maximum_supported_normalized_acquisition is None
|
|
546
|
+
):
|
|
547
|
+
raise ValueError("support bounds must be jointly present")
|
|
548
|
+
_require_probability(
|
|
549
|
+
self.posterior_positive_probability,
|
|
550
|
+
name="posterior_positive_probability",
|
|
551
|
+
)
|
|
552
|
+
_require_probability(
|
|
553
|
+
self.lower_positive_probability,
|
|
554
|
+
name="lower_positive_probability",
|
|
555
|
+
)
|
|
556
|
+
if (
|
|
557
|
+
self.lower_positive_probability
|
|
558
|
+
> self.posterior_positive_probability
|
|
559
|
+
):
|
|
560
|
+
raise ValueError("lower probability exceeds posterior mean")
|
|
561
|
+
if self.abstained:
|
|
562
|
+
if self.abstention_reason is None:
|
|
563
|
+
raise ValueError("abstention requires a reason")
|
|
564
|
+
_require_token(
|
|
565
|
+
self.abstention_reason,
|
|
566
|
+
name="abstention_reason",
|
|
567
|
+
)
|
|
568
|
+
elif self.abstention_reason is not None:
|
|
569
|
+
raise ValueError(
|
|
570
|
+
"a calibrated recommendation cannot have an abstention reason"
|
|
571
|
+
)
|
|
572
|
+
object.__setattr__(
|
|
573
|
+
self,
|
|
574
|
+
"result_sha256",
|
|
575
|
+
_hash(_RESULT_DOMAIN, self._unsigned_record()),
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
579
|
+
return {
|
|
580
|
+
"schema_version": 1,
|
|
581
|
+
"action_sha256": self.action_sha256,
|
|
582
|
+
"calibration": {
|
|
583
|
+
"calibration_id": self.calibration_id,
|
|
584
|
+
"calibration_version": self.calibration_version,
|
|
585
|
+
"definition_sha256": (
|
|
586
|
+
self.calibration_definition_sha256
|
|
587
|
+
),
|
|
588
|
+
},
|
|
589
|
+
"observation_snapshot_sha256": (
|
|
590
|
+
self.observation_snapshot_sha256
|
|
591
|
+
),
|
|
592
|
+
"selected_stratum": self.selected_stratum,
|
|
593
|
+
"stratum_support_count": self.stratum_support_count,
|
|
594
|
+
"skill_support_count": self.skill_support_count,
|
|
595
|
+
"prequential_rank_skill_hex": (
|
|
596
|
+
None
|
|
597
|
+
if self.prequential_rank_skill is None
|
|
598
|
+
else self.prequential_rank_skill.hex()
|
|
599
|
+
),
|
|
600
|
+
"raw_normalized_acquisition_hex": (
|
|
601
|
+
self.raw_normalized_acquisition.hex()
|
|
602
|
+
),
|
|
603
|
+
"minimum_supported_normalized_acquisition_hex": (
|
|
604
|
+
None
|
|
605
|
+
if self.minimum_supported_normalized_acquisition is None
|
|
606
|
+
else self.minimum_supported_normalized_acquisition.hex()
|
|
607
|
+
),
|
|
608
|
+
"maximum_supported_normalized_acquisition_hex": (
|
|
609
|
+
None
|
|
610
|
+
if self.maximum_supported_normalized_acquisition is None
|
|
611
|
+
else self.maximum_supported_normalized_acquisition.hex()
|
|
612
|
+
),
|
|
613
|
+
"support_log_distance_hex": self.support_log_distance.hex(),
|
|
614
|
+
"positive_count": self.positive_count,
|
|
615
|
+
"posterior_positive_probability_hex": (
|
|
616
|
+
self.posterior_positive_probability.hex()
|
|
617
|
+
),
|
|
618
|
+
"lower_positive_probability_hex": (
|
|
619
|
+
self.lower_positive_probability.hex()
|
|
620
|
+
),
|
|
621
|
+
"conformal_lower_gain_hex": self.conformal_lower_gain.hex(),
|
|
622
|
+
"lower_expected_gain_hex": self.lower_expected_gain.hex(),
|
|
623
|
+
"calibrated_acquisition_value_hex": (
|
|
624
|
+
self.calibrated_acquisition_value.hex()
|
|
625
|
+
),
|
|
626
|
+
"calibrated_upper_gain_hex": (
|
|
627
|
+
self.calibrated_upper_gain.hex()
|
|
628
|
+
),
|
|
629
|
+
"abstained": self.abstained,
|
|
630
|
+
"abstention_reason": self.abstention_reason,
|
|
631
|
+
"eligible_candidate_outcomes_observed": False,
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
def to_record(self) -> dict[str, object]:
|
|
635
|
+
self.__post_init__()
|
|
636
|
+
return {
|
|
637
|
+
**self._unsigned_record(),
|
|
638
|
+
"result_sha256": self.result_sha256,
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
@runtime_checkable
|
|
643
|
+
class ArchiveOpportunityCalibrationPort(Protocol):
|
|
644
|
+
"""Stable inverted API for prior-only opportunity calibration."""
|
|
645
|
+
|
|
646
|
+
calibration_id: str
|
|
647
|
+
calibration_version: int
|
|
648
|
+
definition_sha256: str
|
|
649
|
+
|
|
650
|
+
def calibrate(
|
|
651
|
+
self,
|
|
652
|
+
request: ArchiveOpportunityCalibrationRequest,
|
|
653
|
+
) -> ArchiveOpportunityCalibrationResult: ...
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def validate_archive_opportunity_calibration_port(
|
|
657
|
+
value: ArchiveOpportunityCalibrationPort,
|
|
658
|
+
) -> tuple[str, int, str]:
|
|
659
|
+
if not isinstance(value, ArchiveOpportunityCalibrationPort):
|
|
660
|
+
raise TypeError(
|
|
661
|
+
"calibration must implement ArchiveOpportunityCalibrationPort"
|
|
662
|
+
)
|
|
663
|
+
identity = (
|
|
664
|
+
value.calibration_id,
|
|
665
|
+
value.calibration_version,
|
|
666
|
+
value.definition_sha256,
|
|
667
|
+
)
|
|
668
|
+
_require_token(identity[0], name="calibration_id")
|
|
669
|
+
if type(identity[1]) is not int or identity[1] <= 0:
|
|
670
|
+
raise ValueError("calibration_version must be positive")
|
|
671
|
+
require_sha256(identity[2], "calibration definition_sha256")
|
|
672
|
+
return identity
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
@dataclass(frozen=True, slots=True)
|
|
676
|
+
class HierarchicalPrequentialArchiveOpportunityCalibration:
|
|
677
|
+
"""Calibrate raw opportunity using a sealed prior observation snapshot."""
|
|
678
|
+
|
|
679
|
+
observations: tuple[ArchiveOpportunityCalibrationObservation, ...]
|
|
680
|
+
maximum_evidence_cutoff_ordinal: int
|
|
681
|
+
minimum_global_support: int = 6
|
|
682
|
+
minimum_cell_support: int = 4
|
|
683
|
+
minimum_rank_support: int = 4
|
|
684
|
+
minimum_rank_skill: float = 0.0
|
|
685
|
+
residual_lower_probability: float = 0.1
|
|
686
|
+
residual_upper_probability: float = 0.9
|
|
687
|
+
positive_prior_alpha: float = 1.0
|
|
688
|
+
positive_prior_beta: float = 1.0
|
|
689
|
+
wilson_z_value: float = 1.0
|
|
690
|
+
maximum_support_log_distance: float = 1.0
|
|
691
|
+
minimum_lower_expected_gain: float = 0.0
|
|
692
|
+
scale_floor: float = 1e-15
|
|
693
|
+
calibration_id: str = (
|
|
694
|
+
PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID
|
|
695
|
+
)
|
|
696
|
+
calibration_version: int = (
|
|
697
|
+
PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION
|
|
698
|
+
)
|
|
699
|
+
observation_snapshot_sha256: str = field(init=False)
|
|
700
|
+
definition_sha256: str = field(init=False)
|
|
701
|
+
|
|
702
|
+
def __post_init__(self) -> None:
|
|
703
|
+
if (
|
|
704
|
+
type(self.observations) is not tuple
|
|
705
|
+
or any(
|
|
706
|
+
type(value)
|
|
707
|
+
is not ArchiveOpportunityCalibrationObservation
|
|
708
|
+
for value in self.observations
|
|
709
|
+
)
|
|
710
|
+
):
|
|
711
|
+
raise TypeError("observations must contain exact observations")
|
|
712
|
+
for value in self.observations:
|
|
713
|
+
value.__post_init__()
|
|
714
|
+
hashes = tuple(value.observation_sha256 for value in self.observations)
|
|
715
|
+
if hashes != tuple(sorted(set(hashes))):
|
|
716
|
+
raise ValueError(
|
|
717
|
+
"observations must be unique and hash canonical"
|
|
718
|
+
)
|
|
719
|
+
if (
|
|
720
|
+
type(self.maximum_evidence_cutoff_ordinal) is not int
|
|
721
|
+
or self.maximum_evidence_cutoff_ordinal <= 0
|
|
722
|
+
):
|
|
723
|
+
raise ValueError(
|
|
724
|
+
"maximum_evidence_cutoff_ordinal must be positive"
|
|
725
|
+
)
|
|
726
|
+
if any(
|
|
727
|
+
value.evidence_cutoff_ordinal
|
|
728
|
+
> self.maximum_evidence_cutoff_ordinal
|
|
729
|
+
for value in self.observations
|
|
730
|
+
):
|
|
731
|
+
raise ValueError(
|
|
732
|
+
"observation crosses the sealed evidence cutoff"
|
|
733
|
+
)
|
|
734
|
+
for name in (
|
|
735
|
+
"minimum_global_support",
|
|
736
|
+
"minimum_cell_support",
|
|
737
|
+
"minimum_rank_support",
|
|
738
|
+
):
|
|
739
|
+
value = getattr(self, name)
|
|
740
|
+
if type(value) is not int or value <= 0:
|
|
741
|
+
raise ValueError(f"{name} must be positive")
|
|
742
|
+
if (
|
|
743
|
+
type(self.minimum_rank_skill) is not float
|
|
744
|
+
or not math.isfinite(self.minimum_rank_skill)
|
|
745
|
+
or not -1.0 <= self.minimum_rank_skill <= 1.0
|
|
746
|
+
):
|
|
747
|
+
raise ValueError("minimum_rank_skill must lie in [-1,1]")
|
|
748
|
+
for name in (
|
|
749
|
+
"residual_lower_probability",
|
|
750
|
+
"residual_upper_probability",
|
|
751
|
+
):
|
|
752
|
+
_require_probability(getattr(self, name), name=name)
|
|
753
|
+
if (
|
|
754
|
+
self.residual_lower_probability
|
|
755
|
+
>= self.residual_upper_probability
|
|
756
|
+
):
|
|
757
|
+
raise ValueError("residual quantile order is invalid")
|
|
758
|
+
for name in (
|
|
759
|
+
"positive_prior_alpha",
|
|
760
|
+
"positive_prior_beta",
|
|
761
|
+
"wilson_z_value",
|
|
762
|
+
"maximum_support_log_distance",
|
|
763
|
+
"scale_floor",
|
|
764
|
+
):
|
|
765
|
+
value = getattr(self, name)
|
|
766
|
+
if type(value) is not float or not math.isfinite(value) or value <= 0.0:
|
|
767
|
+
raise ValueError(f"{name} must be positive and finite")
|
|
768
|
+
_require_nonnegative(
|
|
769
|
+
self.minimum_lower_expected_gain,
|
|
770
|
+
name="minimum_lower_expected_gain",
|
|
771
|
+
)
|
|
772
|
+
_require_token(self.calibration_id, name="calibration_id")
|
|
773
|
+
if (
|
|
774
|
+
self.calibration_id
|
|
775
|
+
!= PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID
|
|
776
|
+
or self.calibration_version
|
|
777
|
+
!= PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION
|
|
778
|
+
):
|
|
779
|
+
raise ValueError("calibration identity is immutable")
|
|
780
|
+
snapshot = _hash(
|
|
781
|
+
_SNAPSHOT_DOMAIN,
|
|
782
|
+
{
|
|
783
|
+
"schema_version": 1,
|
|
784
|
+
"maximum_evidence_cutoff_ordinal": (
|
|
785
|
+
self.maximum_evidence_cutoff_ordinal
|
|
786
|
+
),
|
|
787
|
+
"observation_sha256s": list(hashes),
|
|
788
|
+
"forbidden_feature_fields": [
|
|
789
|
+
"workload_id",
|
|
790
|
+
"objective_id",
|
|
791
|
+
"provider_id",
|
|
792
|
+
"prompt_id",
|
|
793
|
+
],
|
|
794
|
+
},
|
|
795
|
+
)
|
|
796
|
+
object.__setattr__(
|
|
797
|
+
self,
|
|
798
|
+
"observation_snapshot_sha256",
|
|
799
|
+
snapshot,
|
|
800
|
+
)
|
|
801
|
+
object.__setattr__(
|
|
802
|
+
self,
|
|
803
|
+
"definition_sha256",
|
|
804
|
+
_hash(
|
|
805
|
+
_DEFINITION_DOMAIN,
|
|
806
|
+
{
|
|
807
|
+
"schema_version": 1,
|
|
808
|
+
"calibration_id": self.calibration_id,
|
|
809
|
+
"calibration_version": self.calibration_version,
|
|
810
|
+
"observation_snapshot_sha256": snapshot,
|
|
811
|
+
"minimum_global_support": self.minimum_global_support,
|
|
812
|
+
"minimum_cell_support": self.minimum_cell_support,
|
|
813
|
+
"minimum_rank_support": self.minimum_rank_support,
|
|
814
|
+
"minimum_rank_skill_hex": (
|
|
815
|
+
self.minimum_rank_skill.hex()
|
|
816
|
+
),
|
|
817
|
+
"residual_lower_probability_hex": (
|
|
818
|
+
self.residual_lower_probability.hex()
|
|
819
|
+
),
|
|
820
|
+
"residual_upper_probability_hex": (
|
|
821
|
+
self.residual_upper_probability.hex()
|
|
822
|
+
),
|
|
823
|
+
"positive_prior_alpha_hex": (
|
|
824
|
+
self.positive_prior_alpha.hex()
|
|
825
|
+
),
|
|
826
|
+
"positive_prior_beta_hex": (
|
|
827
|
+
self.positive_prior_beta.hex()
|
|
828
|
+
),
|
|
829
|
+
"wilson_z_value_hex": self.wilson_z_value.hex(),
|
|
830
|
+
"maximum_support_log_distance_hex": (
|
|
831
|
+
self.maximum_support_log_distance.hex()
|
|
832
|
+
),
|
|
833
|
+
"minimum_lower_expected_gain_hex": (
|
|
834
|
+
self.minimum_lower_expected_gain.hex()
|
|
835
|
+
),
|
|
836
|
+
"scale_floor_hex": self.scale_floor.hex(),
|
|
837
|
+
"normalization": (
|
|
838
|
+
"raw_and_realized_gain_over_observed_prefix_mean_gain"
|
|
839
|
+
),
|
|
840
|
+
"residual": "log1p_realized_minus_log1p_raw",
|
|
841
|
+
"strata": [
|
|
842
|
+
"stage_lane_operator",
|
|
843
|
+
"stage_operator",
|
|
844
|
+
"stage",
|
|
845
|
+
"global",
|
|
846
|
+
],
|
|
847
|
+
"skill": "prequential_spearman_raw_vs_realized",
|
|
848
|
+
"lower_bound": (
|
|
849
|
+
"wilson_positive_probability_times_positive_"
|
|
850
|
+
"residual_lower_magnitude"
|
|
851
|
+
),
|
|
852
|
+
"eligible_candidate_outcomes_observed": False,
|
|
853
|
+
"workload_objective_provider_prompt_branches": False,
|
|
854
|
+
},
|
|
855
|
+
),
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
def _scale(
|
|
859
|
+
self,
|
|
860
|
+
request: ArchiveOpportunityCalibrationRequest,
|
|
861
|
+
) -> float:
|
|
862
|
+
return max(self.scale_floor, request.prefix_mean_gain)
|
|
863
|
+
|
|
864
|
+
def _rows(
|
|
865
|
+
self,
|
|
866
|
+
request: ArchiveOpportunityCalibrationRequest,
|
|
867
|
+
) -> tuple[
|
|
868
|
+
str,
|
|
869
|
+
tuple[ArchiveOpportunityCalibrationObservation, ...],
|
|
870
|
+
]:
|
|
871
|
+
context = request.context
|
|
872
|
+
candidates = (
|
|
873
|
+
(
|
|
874
|
+
"stage_lane_operator",
|
|
875
|
+
tuple(
|
|
876
|
+
value
|
|
877
|
+
for value in self.observations
|
|
878
|
+
if (
|
|
879
|
+
value.request.context.decision_index
|
|
880
|
+
== context.decision_index
|
|
881
|
+
and value.request.context.lane_id
|
|
882
|
+
== context.lane_id
|
|
883
|
+
and value.request.context.operator_id
|
|
884
|
+
== context.operator_id
|
|
885
|
+
)
|
|
886
|
+
),
|
|
887
|
+
),
|
|
888
|
+
(
|
|
889
|
+
"stage_operator",
|
|
890
|
+
tuple(
|
|
891
|
+
value
|
|
892
|
+
for value in self.observations
|
|
893
|
+
if (
|
|
894
|
+
value.request.context.decision_index
|
|
895
|
+
== context.decision_index
|
|
896
|
+
and value.request.context.operator_id
|
|
897
|
+
== context.operator_id
|
|
898
|
+
)
|
|
899
|
+
),
|
|
900
|
+
),
|
|
901
|
+
(
|
|
902
|
+
"stage",
|
|
903
|
+
tuple(
|
|
904
|
+
value
|
|
905
|
+
for value in self.observations
|
|
906
|
+
if value.request.context.decision_index
|
|
907
|
+
== context.decision_index
|
|
908
|
+
),
|
|
909
|
+
),
|
|
910
|
+
)
|
|
911
|
+
for name, rows in candidates:
|
|
912
|
+
if len(rows) >= self.minimum_cell_support:
|
|
913
|
+
return name, rows
|
|
914
|
+
return "global", self.observations
|
|
915
|
+
|
|
916
|
+
def _normalized(
|
|
917
|
+
self,
|
|
918
|
+
observation: ArchiveOpportunityCalibrationObservation,
|
|
919
|
+
) -> tuple[float, float]:
|
|
920
|
+
scale = self._scale(observation.request)
|
|
921
|
+
return (
|
|
922
|
+
observation.request.raw_acquisition_value / scale,
|
|
923
|
+
observation.realized_conditional_gain / scale,
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
def calibrate(
|
|
927
|
+
self,
|
|
928
|
+
request: ArchiveOpportunityCalibrationRequest,
|
|
929
|
+
) -> ArchiveOpportunityCalibrationResult:
|
|
930
|
+
self.__post_init__()
|
|
931
|
+
if type(request) is not ArchiveOpportunityCalibrationRequest:
|
|
932
|
+
raise TypeError("request must be exact")
|
|
933
|
+
request.__post_init__()
|
|
934
|
+
stratum, rows = self._rows(request)
|
|
935
|
+
stage_rows = tuple(
|
|
936
|
+
value
|
|
937
|
+
for value in self.observations
|
|
938
|
+
if (
|
|
939
|
+
value.request.context.decision_index
|
|
940
|
+
== request.context.decision_index
|
|
941
|
+
)
|
|
942
|
+
)
|
|
943
|
+
skill_rows = (
|
|
944
|
+
stage_rows
|
|
945
|
+
if len(stage_rows) >= self.minimum_rank_support
|
|
946
|
+
else self.observations
|
|
947
|
+
)
|
|
948
|
+
rank_skill = (
|
|
949
|
+
_spearman(
|
|
950
|
+
tuple(
|
|
951
|
+
self._normalized(value)[0]
|
|
952
|
+
for value in skill_rows
|
|
953
|
+
),
|
|
954
|
+
tuple(
|
|
955
|
+
self._normalized(value)[1]
|
|
956
|
+
for value in skill_rows
|
|
957
|
+
),
|
|
958
|
+
)
|
|
959
|
+
if len(skill_rows) >= self.minimum_rank_support
|
|
960
|
+
else None
|
|
961
|
+
)
|
|
962
|
+
scale = self._scale(request)
|
|
963
|
+
raw_normalized = request.raw_acquisition_value / scale
|
|
964
|
+
|
|
965
|
+
if len(self.observations) < self.minimum_global_support:
|
|
966
|
+
abstention_reason = "insufficient_global_support"
|
|
967
|
+
elif len(rows) < self.minimum_cell_support:
|
|
968
|
+
abstention_reason = "insufficient_cell_support"
|
|
969
|
+
elif (
|
|
970
|
+
rank_skill is None
|
|
971
|
+
or len(skill_rows) < self.minimum_rank_support
|
|
972
|
+
):
|
|
973
|
+
abstention_reason = "insufficient_rank_skill_support"
|
|
974
|
+
elif rank_skill <= self.minimum_rank_skill:
|
|
975
|
+
abstention_reason = "nonpositive_prequential_rank_skill"
|
|
976
|
+
else:
|
|
977
|
+
abstention_reason = None
|
|
978
|
+
|
|
979
|
+
normalized_rows = tuple(self._normalized(value) for value in rows)
|
|
980
|
+
supported_raw = tuple(value[0] for value in normalized_rows)
|
|
981
|
+
minimum_supported = min(supported_raw) if supported_raw else None
|
|
982
|
+
maximum_supported = max(supported_raw) if supported_raw else None
|
|
983
|
+
support_log_distance = 0.0
|
|
984
|
+
if minimum_supported is not None and maximum_supported is not None:
|
|
985
|
+
log_raw = math.log1p(raw_normalized)
|
|
986
|
+
log_minimum = math.log1p(minimum_supported)
|
|
987
|
+
log_maximum = math.log1p(maximum_supported)
|
|
988
|
+
support_log_distance = max(
|
|
989
|
+
0.0,
|
|
990
|
+
log_minimum - log_raw,
|
|
991
|
+
log_raw - log_maximum,
|
|
992
|
+
)
|
|
993
|
+
if (
|
|
994
|
+
abstention_reason is None
|
|
995
|
+
and support_log_distance
|
|
996
|
+
> self.maximum_support_log_distance
|
|
997
|
+
):
|
|
998
|
+
abstention_reason = "forecast_scale_out_of_support"
|
|
999
|
+
|
|
1000
|
+
residuals = tuple(
|
|
1001
|
+
math.log1p(realized) - math.log1p(raw)
|
|
1002
|
+
for raw, realized in normalized_rows
|
|
1003
|
+
)
|
|
1004
|
+
positive_rows = tuple(
|
|
1005
|
+
(raw, realized)
|
|
1006
|
+
for raw, realized in normalized_rows
|
|
1007
|
+
if realized > 0.0
|
|
1008
|
+
)
|
|
1009
|
+
positive_count = len(positive_rows)
|
|
1010
|
+
posterior_positive = (
|
|
1011
|
+
self.positive_prior_alpha + positive_count
|
|
1012
|
+
) / (
|
|
1013
|
+
self.positive_prior_alpha
|
|
1014
|
+
+ self.positive_prior_beta
|
|
1015
|
+
+ len(rows)
|
|
1016
|
+
)
|
|
1017
|
+
lower_positive = (
|
|
1018
|
+
_wilson_lower_bound(
|
|
1019
|
+
positive_count=positive_count,
|
|
1020
|
+
observation_count=len(rows),
|
|
1021
|
+
z_value=self.wilson_z_value,
|
|
1022
|
+
)
|
|
1023
|
+
if rows
|
|
1024
|
+
else 0.0
|
|
1025
|
+
)
|
|
1026
|
+
|
|
1027
|
+
if residuals:
|
|
1028
|
+
lower_residual = _nearest_rank_quantile(
|
|
1029
|
+
residuals,
|
|
1030
|
+
self.residual_lower_probability,
|
|
1031
|
+
)
|
|
1032
|
+
conformal_lower_normalized = max(
|
|
1033
|
+
0.0,
|
|
1034
|
+
math.expm1(
|
|
1035
|
+
math.log1p(raw_normalized) + lower_residual
|
|
1036
|
+
),
|
|
1037
|
+
)
|
|
1038
|
+
else:
|
|
1039
|
+
conformal_lower_normalized = 0.0
|
|
1040
|
+
|
|
1041
|
+
if positive_rows:
|
|
1042
|
+
positive_residuals = tuple(
|
|
1043
|
+
math.log1p(realized) - math.log1p(raw)
|
|
1044
|
+
for raw, realized in positive_rows
|
|
1045
|
+
)
|
|
1046
|
+
lower_positive_residual = _nearest_rank_quantile(
|
|
1047
|
+
positive_residuals,
|
|
1048
|
+
self.residual_lower_probability,
|
|
1049
|
+
)
|
|
1050
|
+
median_positive_residual = _nearest_rank_quantile(
|
|
1051
|
+
positive_residuals,
|
|
1052
|
+
0.5,
|
|
1053
|
+
)
|
|
1054
|
+
upper_positive_residual = _nearest_rank_quantile(
|
|
1055
|
+
positive_residuals,
|
|
1056
|
+
self.residual_upper_probability,
|
|
1057
|
+
)
|
|
1058
|
+
positive_lower_normalized = max(
|
|
1059
|
+
0.0,
|
|
1060
|
+
math.expm1(
|
|
1061
|
+
math.log1p(raw_normalized)
|
|
1062
|
+
+ lower_positive_residual
|
|
1063
|
+
),
|
|
1064
|
+
)
|
|
1065
|
+
positive_median_normalized = max(
|
|
1066
|
+
0.0,
|
|
1067
|
+
math.expm1(
|
|
1068
|
+
math.log1p(raw_normalized)
|
|
1069
|
+
+ median_positive_residual
|
|
1070
|
+
),
|
|
1071
|
+
)
|
|
1072
|
+
positive_upper_normalized = max(
|
|
1073
|
+
0.0,
|
|
1074
|
+
math.expm1(
|
|
1075
|
+
math.log1p(raw_normalized)
|
|
1076
|
+
+ upper_positive_residual
|
|
1077
|
+
),
|
|
1078
|
+
)
|
|
1079
|
+
else:
|
|
1080
|
+
positive_lower_normalized = 0.0
|
|
1081
|
+
positive_median_normalized = 0.0
|
|
1082
|
+
positive_upper_normalized = 0.0
|
|
1083
|
+
if abstention_reason is None:
|
|
1084
|
+
abstention_reason = "no_positive_calibration_support"
|
|
1085
|
+
|
|
1086
|
+
conformal_lower_gain = scale * conformal_lower_normalized
|
|
1087
|
+
lower_expected_gain = (
|
|
1088
|
+
scale * lower_positive * positive_lower_normalized
|
|
1089
|
+
)
|
|
1090
|
+
calibrated_acquisition = (
|
|
1091
|
+
scale
|
|
1092
|
+
* posterior_positive
|
|
1093
|
+
* positive_median_normalized
|
|
1094
|
+
)
|
|
1095
|
+
calibrated_upper = scale * positive_upper_normalized
|
|
1096
|
+
if (
|
|
1097
|
+
abstention_reason is None
|
|
1098
|
+
and lower_expected_gain
|
|
1099
|
+
<= self.minimum_lower_expected_gain
|
|
1100
|
+
):
|
|
1101
|
+
abstention_reason = "no_calibrated_lower_opportunity"
|
|
1102
|
+
|
|
1103
|
+
return ArchiveOpportunityCalibrationResult(
|
|
1104
|
+
action_sha256=request.context.action_sha256,
|
|
1105
|
+
calibration_id=self.calibration_id,
|
|
1106
|
+
calibration_version=self.calibration_version,
|
|
1107
|
+
calibration_definition_sha256=self.definition_sha256,
|
|
1108
|
+
observation_snapshot_sha256=(
|
|
1109
|
+
self.observation_snapshot_sha256
|
|
1110
|
+
),
|
|
1111
|
+
selected_stratum=stratum,
|
|
1112
|
+
stratum_support_count=len(rows),
|
|
1113
|
+
skill_support_count=len(skill_rows),
|
|
1114
|
+
prequential_rank_skill=rank_skill,
|
|
1115
|
+
raw_normalized_acquisition=float(raw_normalized),
|
|
1116
|
+
minimum_supported_normalized_acquisition=(
|
|
1117
|
+
None
|
|
1118
|
+
if minimum_supported is None
|
|
1119
|
+
else float(minimum_supported)
|
|
1120
|
+
),
|
|
1121
|
+
maximum_supported_normalized_acquisition=(
|
|
1122
|
+
None
|
|
1123
|
+
if maximum_supported is None
|
|
1124
|
+
else float(maximum_supported)
|
|
1125
|
+
),
|
|
1126
|
+
support_log_distance=float(support_log_distance),
|
|
1127
|
+
positive_count=positive_count,
|
|
1128
|
+
posterior_positive_probability=float(
|
|
1129
|
+
posterior_positive
|
|
1130
|
+
),
|
|
1131
|
+
lower_positive_probability=float(lower_positive),
|
|
1132
|
+
conformal_lower_gain=float(conformal_lower_gain),
|
|
1133
|
+
lower_expected_gain=float(lower_expected_gain),
|
|
1134
|
+
calibrated_acquisition_value=float(
|
|
1135
|
+
calibrated_acquisition
|
|
1136
|
+
),
|
|
1137
|
+
calibrated_upper_gain=float(calibrated_upper),
|
|
1138
|
+
abstained=abstention_reason is not None,
|
|
1139
|
+
abstention_reason=abstention_reason,
|
|
1140
|
+
)
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
__all__ = [
|
|
1144
|
+
"ArchiveOpportunityActionContext",
|
|
1145
|
+
"ArchiveOpportunityCalibrationEvidenceRole",
|
|
1146
|
+
"ArchiveOpportunityCalibrationObservation",
|
|
1147
|
+
"ArchiveOpportunityCalibrationPort",
|
|
1148
|
+
"ArchiveOpportunityCalibrationRequest",
|
|
1149
|
+
"ArchiveOpportunityCalibrationResult",
|
|
1150
|
+
"HierarchicalPrequentialArchiveOpportunityCalibration",
|
|
1151
|
+
"PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_ID",
|
|
1152
|
+
"PREQUENTIAL_ARCHIVE_OPPORTUNITY_CALIBRATION_VERSION",
|
|
1153
|
+
"validate_archive_opportunity_calibration_port",
|
|
1154
|
+
]
|