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,922 @@
|
|
|
1
|
+
"""Authenticated categorical forecast calibration with prior-only snapshots.
|
|
2
|
+
|
|
3
|
+
Numeric metric changes cross only the benchmark-owned adjudicator seam. The
|
|
4
|
+
persistent policy state is a compact set of signed categorical observations,
|
|
5
|
+
filtered at an exclusive wave cutoff before any allocator can consume it.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import math
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from typing import Protocol, Sequence, runtime_checkable
|
|
17
|
+
|
|
18
|
+
from agent_evolve.domain.patch import require_sha256
|
|
19
|
+
from agent_evolve.ports.agentic_generator import MetricEffectDirection
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_SCOPE_DOMAIN = b"agent-evolve:forecast-calibration-scope:v1\x00"
|
|
23
|
+
_PREDICTION_DOMAIN = b"agent-evolve:forecast-prediction-receipt:v1\x00"
|
|
24
|
+
_ADJUDICATION_REQUEST_DOMAIN = (
|
|
25
|
+
b"agent-evolve:meaningful-direction-adjudication-request:v1\x00"
|
|
26
|
+
)
|
|
27
|
+
_ADJUDICATION_DOMAIN = b"agent-evolve:meaningful-direction-adjudication:v1\x00"
|
|
28
|
+
_OBSERVATION_DOMAIN = b"agent-evolve:forecast-calibration-observation:v1\x00"
|
|
29
|
+
_SNAPSHOT_DOMAIN = b"agent-evolve:forecast-calibration-snapshot:v1\x00"
|
|
30
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
|
|
31
|
+
_METRIC = re.compile(r"^[a-z][a-z0-9_.:-]{0,191}$")
|
|
32
|
+
_OPTION = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
33
|
+
_MAX_WAVE = (1 << 63) - 1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _canonical_json(value: object) -> bytes:
|
|
37
|
+
return json.dumps(
|
|
38
|
+
value,
|
|
39
|
+
allow_nan=False,
|
|
40
|
+
ensure_ascii=True,
|
|
41
|
+
separators=(",", ":"),
|
|
42
|
+
sort_keys=True,
|
|
43
|
+
).encode("ascii", errors="strict")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
47
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _require_token(value: str, *, name: str) -> None:
|
|
51
|
+
if type(value) is not str or _TOKEN.fullmatch(value) is None:
|
|
52
|
+
raise ValueError(f"{name} must use the closed lowercase token grammar")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _require_metric(value: str, *, name: str = "metric_id") -> None:
|
|
56
|
+
if type(value) is not str or _METRIC.fullmatch(value) is None:
|
|
57
|
+
raise ValueError(f"{name} must use the closed metric identifier grammar")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _require_option(value: str, *, name: str = "option_id") -> None:
|
|
61
|
+
if type(value) is not str or _OPTION.fullmatch(value) is None:
|
|
62
|
+
raise ValueError(f"{name} must use the closed option identifier grammar")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _require_wave(value: int, *, name: str) -> None:
|
|
66
|
+
if type(value) is not int or not 1 <= value <= _MAX_WAVE:
|
|
67
|
+
raise ValueError(f"{name} must be an exact positive int63")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _require_finite_float(value: float, *, name: str) -> None:
|
|
71
|
+
if type(value) is not float or not math.isfinite(value):
|
|
72
|
+
raise TypeError(f"{name} must be a finite canonical float")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ForecastConfidenceBin(str, Enum):
|
|
76
|
+
"""Closed confidence vocabulary emitted with a direction forecast."""
|
|
77
|
+
|
|
78
|
+
LOW = "low"
|
|
79
|
+
MEDIUM = "medium"
|
|
80
|
+
HIGH = "high"
|
|
81
|
+
UNKNOWN = "unknown"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True, slots=True)
|
|
85
|
+
class ForecastCalibrationScope:
|
|
86
|
+
"""Identity of one local model/prompt/policy/benchmark/session stratum."""
|
|
87
|
+
|
|
88
|
+
model_profile_sha256: str
|
|
89
|
+
prompt_definition_sha256: str
|
|
90
|
+
selector_policy_definition_sha256: str
|
|
91
|
+
benchmark_sha256: str
|
|
92
|
+
session_sha256: str
|
|
93
|
+
|
|
94
|
+
def __post_init__(self) -> None:
|
|
95
|
+
for name in (
|
|
96
|
+
"model_profile_sha256",
|
|
97
|
+
"prompt_definition_sha256",
|
|
98
|
+
"selector_policy_definition_sha256",
|
|
99
|
+
"benchmark_sha256",
|
|
100
|
+
"session_sha256",
|
|
101
|
+
):
|
|
102
|
+
require_sha256(getattr(self, name), name)
|
|
103
|
+
|
|
104
|
+
def revalidate(self) -> None:
|
|
105
|
+
if type(self) is not ForecastCalibrationScope:
|
|
106
|
+
raise TypeError("scope must be exact ForecastCalibrationScope")
|
|
107
|
+
ForecastCalibrationScope.__post_init__(self)
|
|
108
|
+
|
|
109
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
110
|
+
self.revalidate()
|
|
111
|
+
return {
|
|
112
|
+
"schema_version": 1,
|
|
113
|
+
"model_profile_sha256": self.model_profile_sha256,
|
|
114
|
+
"prompt_definition_sha256": self.prompt_definition_sha256,
|
|
115
|
+
"selector_policy_definition_sha256": (
|
|
116
|
+
self.selector_policy_definition_sha256
|
|
117
|
+
),
|
|
118
|
+
"benchmark_sha256": self.benchmark_sha256,
|
|
119
|
+
"session_sha256": self.session_sha256,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def scope_sha256(self) -> str:
|
|
124
|
+
return _hash(_SCOPE_DOMAIN, self._unsigned_record())
|
|
125
|
+
|
|
126
|
+
def for_policy_frame(
|
|
127
|
+
self,
|
|
128
|
+
*,
|
|
129
|
+
prompt_definition_sha256: str,
|
|
130
|
+
selector_policy_definition_sha256: str,
|
|
131
|
+
) -> "ForecastCalibrationScope":
|
|
132
|
+
"""Preserve the experiment stratum while changing policy provenance.
|
|
133
|
+
|
|
134
|
+
One campaign can legitimately contain more than one predictor: for
|
|
135
|
+
example, an engine-owned calibrated slate policy and a runtime
|
|
136
|
+
outcome-conditioned consequence expert. Their observations must not
|
|
137
|
+
share prompt/policy identity, while model, benchmark, and session
|
|
138
|
+
identity must remain exact. This immutable operation makes that
|
|
139
|
+
separation explicit at the generic calibration boundary.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
self.revalidate()
|
|
143
|
+
return ForecastCalibrationScope(
|
|
144
|
+
model_profile_sha256=self.model_profile_sha256,
|
|
145
|
+
prompt_definition_sha256=prompt_definition_sha256,
|
|
146
|
+
selector_policy_definition_sha256=(
|
|
147
|
+
selector_policy_definition_sha256
|
|
148
|
+
),
|
|
149
|
+
benchmark_sha256=self.benchmark_sha256,
|
|
150
|
+
session_sha256=self.session_sha256,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def to_record(self) -> dict[str, object]:
|
|
154
|
+
return {**self._unsigned_record(), "scope_sha256": self.scope_sha256}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass(frozen=True, slots=True)
|
|
158
|
+
class BetaCorrectnessPrior:
|
|
159
|
+
"""Declared shrinkage prior for sparse categorical correctness cells."""
|
|
160
|
+
|
|
161
|
+
alpha: float = 1.0
|
|
162
|
+
beta: float = 1.0
|
|
163
|
+
|
|
164
|
+
def __post_init__(self) -> None:
|
|
165
|
+
_require_finite_float(self.alpha, name="alpha")
|
|
166
|
+
_require_finite_float(self.beta, name="beta")
|
|
167
|
+
if self.alpha <= 0.0 or self.beta <= 0.0:
|
|
168
|
+
raise ValueError("Beta prior parameters must be strictly positive")
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def mean(self) -> float:
|
|
172
|
+
self.__post_init__()
|
|
173
|
+
return self.alpha / (self.alpha + self.beta)
|
|
174
|
+
|
|
175
|
+
def to_record(self) -> dict[str, object]:
|
|
176
|
+
self.__post_init__()
|
|
177
|
+
return {
|
|
178
|
+
"family": "beta_bernoulli_correctness",
|
|
179
|
+
"alpha_hex": self.alpha.hex(),
|
|
180
|
+
"beta_hex": self.beta.hex(),
|
|
181
|
+
"mean_hex": self.mean.hex(),
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dataclass(frozen=True, slots=True)
|
|
186
|
+
class ForecastPredictionReceipt:
|
|
187
|
+
"""Authenticated categorical prediction emitted before evaluation."""
|
|
188
|
+
|
|
189
|
+
scope: ForecastCalibrationScope
|
|
190
|
+
wave_index: int
|
|
191
|
+
selector_decision_sha256: str
|
|
192
|
+
parent_candidate_identity_sha256: str
|
|
193
|
+
option_id: str
|
|
194
|
+
option_identity_sha256: str
|
|
195
|
+
family: str
|
|
196
|
+
metric_id: str
|
|
197
|
+
asserted_direction: MetricEffectDirection
|
|
198
|
+
confidence: ForecastConfidenceBin
|
|
199
|
+
|
|
200
|
+
def __post_init__(self) -> None:
|
|
201
|
+
if type(self.scope) is not ForecastCalibrationScope:
|
|
202
|
+
raise TypeError("scope must be exact ForecastCalibrationScope")
|
|
203
|
+
self.scope.revalidate()
|
|
204
|
+
_require_wave(self.wave_index, name="wave_index")
|
|
205
|
+
for name in (
|
|
206
|
+
"selector_decision_sha256",
|
|
207
|
+
"parent_candidate_identity_sha256",
|
|
208
|
+
"option_identity_sha256",
|
|
209
|
+
):
|
|
210
|
+
require_sha256(getattr(self, name), name)
|
|
211
|
+
_require_option(self.option_id)
|
|
212
|
+
_require_token(self.family, name="family")
|
|
213
|
+
_require_metric(self.metric_id)
|
|
214
|
+
if type(self.asserted_direction) is not MetricEffectDirection:
|
|
215
|
+
raise TypeError("asserted_direction must be exact MetricEffectDirection")
|
|
216
|
+
if type(self.confidence) is not ForecastConfidenceBin:
|
|
217
|
+
raise TypeError("confidence must be exact ForecastConfidenceBin")
|
|
218
|
+
|
|
219
|
+
def revalidate(self) -> None:
|
|
220
|
+
if type(self) is not ForecastPredictionReceipt:
|
|
221
|
+
raise TypeError("prediction must be exact ForecastPredictionReceipt")
|
|
222
|
+
ForecastPredictionReceipt.__post_init__(self)
|
|
223
|
+
|
|
224
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
225
|
+
self.revalidate()
|
|
226
|
+
return {
|
|
227
|
+
"schema_version": 1,
|
|
228
|
+
"scope_sha256": self.scope.scope_sha256,
|
|
229
|
+
"wave_index": self.wave_index,
|
|
230
|
+
"selector_decision_sha256": self.selector_decision_sha256,
|
|
231
|
+
"parent_candidate_identity_sha256": (self.parent_candidate_identity_sha256),
|
|
232
|
+
"option_id": self.option_id,
|
|
233
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
234
|
+
"family": self.family,
|
|
235
|
+
"metric_id": self.metric_id,
|
|
236
|
+
"asserted_direction": self.asserted_direction.value,
|
|
237
|
+
"confidence": self.confidence.value,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def receipt_sha256(self) -> str:
|
|
242
|
+
return _hash(_PREDICTION_DOMAIN, self._unsigned_record())
|
|
243
|
+
|
|
244
|
+
def to_record(self) -> dict[str, object]:
|
|
245
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@dataclass(frozen=True, slots=True)
|
|
249
|
+
class MeaningfulDirectionRequest:
|
|
250
|
+
"""Numeric request interpreted only by a benchmark-owned adjudicator."""
|
|
251
|
+
|
|
252
|
+
benchmark_sha256: str
|
|
253
|
+
session_sha256: str
|
|
254
|
+
wave_index: int
|
|
255
|
+
parent_candidate_identity_sha256: str
|
|
256
|
+
option_id: str
|
|
257
|
+
option_identity_sha256: str
|
|
258
|
+
metric_id: str
|
|
259
|
+
parent_outcome_sha256: str
|
|
260
|
+
child_outcome_sha256: str
|
|
261
|
+
parent_metric_value: float
|
|
262
|
+
child_metric_value: float
|
|
263
|
+
|
|
264
|
+
def __post_init__(self) -> None:
|
|
265
|
+
for name in (
|
|
266
|
+
"benchmark_sha256",
|
|
267
|
+
"session_sha256",
|
|
268
|
+
"parent_candidate_identity_sha256",
|
|
269
|
+
"option_identity_sha256",
|
|
270
|
+
"parent_outcome_sha256",
|
|
271
|
+
"child_outcome_sha256",
|
|
272
|
+
):
|
|
273
|
+
require_sha256(getattr(self, name), name)
|
|
274
|
+
_require_wave(self.wave_index, name="wave_index")
|
|
275
|
+
_require_option(self.option_id)
|
|
276
|
+
_require_metric(self.metric_id)
|
|
277
|
+
_require_finite_float(self.parent_metric_value, name="parent_metric_value")
|
|
278
|
+
_require_finite_float(self.child_metric_value, name="child_metric_value")
|
|
279
|
+
|
|
280
|
+
def revalidate(self) -> None:
|
|
281
|
+
if type(self) is not MeaningfulDirectionRequest:
|
|
282
|
+
raise TypeError("request must be exact MeaningfulDirectionRequest")
|
|
283
|
+
MeaningfulDirectionRequest.__post_init__(self)
|
|
284
|
+
|
|
285
|
+
def _record(self) -> dict[str, object]:
|
|
286
|
+
self.revalidate()
|
|
287
|
+
return {
|
|
288
|
+
"schema_version": 1,
|
|
289
|
+
"benchmark_sha256": self.benchmark_sha256,
|
|
290
|
+
"session_sha256": self.session_sha256,
|
|
291
|
+
"wave_index": self.wave_index,
|
|
292
|
+
"parent_candidate_identity_sha256": (self.parent_candidate_identity_sha256),
|
|
293
|
+
"option_id": self.option_id,
|
|
294
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
295
|
+
"metric_id": self.metric_id,
|
|
296
|
+
"parent_outcome_sha256": self.parent_outcome_sha256,
|
|
297
|
+
"child_outcome_sha256": self.child_outcome_sha256,
|
|
298
|
+
"parent_metric_value_hex": self.parent_metric_value.hex(),
|
|
299
|
+
"child_metric_value_hex": self.child_metric_value.hex(),
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def request_sha256(self) -> str:
|
|
304
|
+
return _hash(_ADJUDICATION_REQUEST_DOMAIN, self._record())
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
@dataclass(frozen=True, slots=True)
|
|
308
|
+
class MeaningfulDirectionAdjudicationReceipt:
|
|
309
|
+
"""Categorical benchmark judgment bound to exact parent/child outcomes."""
|
|
310
|
+
|
|
311
|
+
request_sha256: str
|
|
312
|
+
benchmark_sha256: str
|
|
313
|
+
session_sha256: str
|
|
314
|
+
wave_index: int
|
|
315
|
+
parent_candidate_identity_sha256: str
|
|
316
|
+
option_id: str
|
|
317
|
+
option_identity_sha256: str
|
|
318
|
+
metric_id: str
|
|
319
|
+
parent_outcome_sha256: str
|
|
320
|
+
child_outcome_sha256: str
|
|
321
|
+
actual_direction: MetricEffectDirection
|
|
322
|
+
adjudicator_policy_id: str
|
|
323
|
+
adjudicator_policy_version: int
|
|
324
|
+
adjudicator_definition_sha256: str
|
|
325
|
+
|
|
326
|
+
def __post_init__(self) -> None:
|
|
327
|
+
for name in (
|
|
328
|
+
"request_sha256",
|
|
329
|
+
"benchmark_sha256",
|
|
330
|
+
"session_sha256",
|
|
331
|
+
"parent_candidate_identity_sha256",
|
|
332
|
+
"option_identity_sha256",
|
|
333
|
+
"parent_outcome_sha256",
|
|
334
|
+
"child_outcome_sha256",
|
|
335
|
+
"adjudicator_definition_sha256",
|
|
336
|
+
):
|
|
337
|
+
require_sha256(getattr(self, name), name)
|
|
338
|
+
_require_wave(self.wave_index, name="wave_index")
|
|
339
|
+
_require_option(self.option_id)
|
|
340
|
+
_require_metric(self.metric_id)
|
|
341
|
+
if (
|
|
342
|
+
type(self.actual_direction) is not MetricEffectDirection
|
|
343
|
+
or self.actual_direction is MetricEffectDirection.UNKNOWN
|
|
344
|
+
):
|
|
345
|
+
raise ValueError("actual_direction must be a known metric direction")
|
|
346
|
+
_require_token(self.adjudicator_policy_id, name="adjudicator_policy_id")
|
|
347
|
+
if (
|
|
348
|
+
type(self.adjudicator_policy_version) is not int
|
|
349
|
+
or self.adjudicator_policy_version <= 0
|
|
350
|
+
):
|
|
351
|
+
raise ValueError("adjudicator_policy_version must be positive")
|
|
352
|
+
|
|
353
|
+
def revalidate(self) -> None:
|
|
354
|
+
if type(self) is not MeaningfulDirectionAdjudicationReceipt:
|
|
355
|
+
raise TypeError(
|
|
356
|
+
"adjudication must be exact MeaningfulDirectionAdjudicationReceipt"
|
|
357
|
+
)
|
|
358
|
+
MeaningfulDirectionAdjudicationReceipt.__post_init__(self)
|
|
359
|
+
|
|
360
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
361
|
+
self.revalidate()
|
|
362
|
+
return {
|
|
363
|
+
"schema_version": 1,
|
|
364
|
+
"request_sha256": self.request_sha256,
|
|
365
|
+
"benchmark_sha256": self.benchmark_sha256,
|
|
366
|
+
"session_sha256": self.session_sha256,
|
|
367
|
+
"wave_index": self.wave_index,
|
|
368
|
+
"parent_candidate_identity_sha256": (self.parent_candidate_identity_sha256),
|
|
369
|
+
"option_id": self.option_id,
|
|
370
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
371
|
+
"metric_id": self.metric_id,
|
|
372
|
+
"parent_outcome_sha256": self.parent_outcome_sha256,
|
|
373
|
+
"child_outcome_sha256": self.child_outcome_sha256,
|
|
374
|
+
"actual_direction": self.actual_direction.value,
|
|
375
|
+
"adjudicator": {
|
|
376
|
+
"policy_id": self.adjudicator_policy_id,
|
|
377
|
+
"policy_version": self.adjudicator_policy_version,
|
|
378
|
+
"definition_sha256": self.adjudicator_definition_sha256,
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@property
|
|
383
|
+
def receipt_sha256(self) -> str:
|
|
384
|
+
return _hash(_ADJUDICATION_DOMAIN, self._unsigned_record())
|
|
385
|
+
|
|
386
|
+
def to_record(self) -> dict[str, object]:
|
|
387
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
388
|
+
|
|
389
|
+
def require_request(self, request: MeaningfulDirectionRequest) -> None:
|
|
390
|
+
"""Authenticate this categorical result against its numeric request."""
|
|
391
|
+
|
|
392
|
+
self.revalidate()
|
|
393
|
+
if type(request) is not MeaningfulDirectionRequest:
|
|
394
|
+
raise TypeError("request must be exact MeaningfulDirectionRequest")
|
|
395
|
+
request.revalidate()
|
|
396
|
+
observed = (
|
|
397
|
+
self.request_sha256,
|
|
398
|
+
self.benchmark_sha256,
|
|
399
|
+
self.session_sha256,
|
|
400
|
+
self.wave_index,
|
|
401
|
+
self.parent_candidate_identity_sha256,
|
|
402
|
+
self.option_id,
|
|
403
|
+
self.option_identity_sha256,
|
|
404
|
+
self.metric_id,
|
|
405
|
+
self.parent_outcome_sha256,
|
|
406
|
+
self.child_outcome_sha256,
|
|
407
|
+
)
|
|
408
|
+
expected = (
|
|
409
|
+
request.request_sha256,
|
|
410
|
+
request.benchmark_sha256,
|
|
411
|
+
request.session_sha256,
|
|
412
|
+
request.wave_index,
|
|
413
|
+
request.parent_candidate_identity_sha256,
|
|
414
|
+
request.option_id,
|
|
415
|
+
request.option_identity_sha256,
|
|
416
|
+
request.metric_id,
|
|
417
|
+
request.parent_outcome_sha256,
|
|
418
|
+
request.child_outcome_sha256,
|
|
419
|
+
)
|
|
420
|
+
if observed != expected:
|
|
421
|
+
raise ValueError("adjudication receipt belongs to a foreign request")
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
@runtime_checkable
|
|
425
|
+
class MeaningfulMetricDirectionAdjudicator(Protocol):
|
|
426
|
+
"""Inverted benchmark seam for meaningful parent/child direction."""
|
|
427
|
+
|
|
428
|
+
policy_id: str
|
|
429
|
+
policy_version: int
|
|
430
|
+
definition_sha256: str
|
|
431
|
+
|
|
432
|
+
def adjudicate(
|
|
433
|
+
self, request: MeaningfulDirectionRequest
|
|
434
|
+
) -> MeaningfulDirectionAdjudicationReceipt: ...
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@dataclass(frozen=True, slots=True)
|
|
438
|
+
class ForecastCalibrationObservation:
|
|
439
|
+
"""One pre-evaluation prediction joined to one authenticated outcome fact."""
|
|
440
|
+
|
|
441
|
+
prediction: ForecastPredictionReceipt
|
|
442
|
+
adjudication: MeaningfulDirectionAdjudicationReceipt
|
|
443
|
+
|
|
444
|
+
def __post_init__(self) -> None:
|
|
445
|
+
if type(self.prediction) is not ForecastPredictionReceipt:
|
|
446
|
+
raise TypeError("prediction must be exact ForecastPredictionReceipt")
|
|
447
|
+
if type(self.adjudication) is not MeaningfulDirectionAdjudicationReceipt:
|
|
448
|
+
raise TypeError(
|
|
449
|
+
"adjudication must be exact MeaningfulDirectionAdjudicationReceipt"
|
|
450
|
+
)
|
|
451
|
+
self.prediction.revalidate()
|
|
452
|
+
self.adjudication.revalidate()
|
|
453
|
+
scope = self.prediction.scope
|
|
454
|
+
observed = (
|
|
455
|
+
scope.benchmark_sha256,
|
|
456
|
+
scope.session_sha256,
|
|
457
|
+
self.prediction.wave_index,
|
|
458
|
+
self.prediction.parent_candidate_identity_sha256,
|
|
459
|
+
self.prediction.option_id,
|
|
460
|
+
self.prediction.option_identity_sha256,
|
|
461
|
+
self.prediction.metric_id,
|
|
462
|
+
)
|
|
463
|
+
expected = (
|
|
464
|
+
self.adjudication.benchmark_sha256,
|
|
465
|
+
self.adjudication.session_sha256,
|
|
466
|
+
self.adjudication.wave_index,
|
|
467
|
+
self.adjudication.parent_candidate_identity_sha256,
|
|
468
|
+
self.adjudication.option_id,
|
|
469
|
+
self.adjudication.option_identity_sha256,
|
|
470
|
+
self.adjudication.metric_id,
|
|
471
|
+
)
|
|
472
|
+
if observed != expected:
|
|
473
|
+
raise ValueError("prediction and adjudication evidence do not join")
|
|
474
|
+
|
|
475
|
+
def revalidate(self) -> None:
|
|
476
|
+
if type(self) is not ForecastCalibrationObservation:
|
|
477
|
+
raise TypeError("observation must be exact ForecastCalibrationObservation")
|
|
478
|
+
ForecastCalibrationObservation.__post_init__(self)
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def is_abstention(self) -> bool:
|
|
482
|
+
return self.prediction.asserted_direction is MetricEffectDirection.UNKNOWN
|
|
483
|
+
|
|
484
|
+
@property
|
|
485
|
+
def correctness(self) -> bool | None:
|
|
486
|
+
if self.is_abstention:
|
|
487
|
+
return None
|
|
488
|
+
return self.prediction.asserted_direction is self.adjudication.actual_direction
|
|
489
|
+
|
|
490
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
491
|
+
self.revalidate()
|
|
492
|
+
return {
|
|
493
|
+
"schema_version": 1,
|
|
494
|
+
"prediction_receipt": self.prediction.to_record(),
|
|
495
|
+
"adjudication_receipt": self.adjudication.to_record(),
|
|
496
|
+
"is_abstention": self.is_abstention,
|
|
497
|
+
"correctness": self.correctness,
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
@property
|
|
501
|
+
def observation_sha256(self) -> str:
|
|
502
|
+
return _hash(_OBSERVATION_DOMAIN, self._unsigned_record())
|
|
503
|
+
|
|
504
|
+
def to_record(self) -> dict[str, object]:
|
|
505
|
+
return {
|
|
506
|
+
**self._unsigned_record(),
|
|
507
|
+
"observation_sha256": self.observation_sha256,
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def observe_forecast(
|
|
512
|
+
prediction: ForecastPredictionReceipt,
|
|
513
|
+
request: MeaningfulDirectionRequest,
|
|
514
|
+
adjudicator: MeaningfulMetricDirectionAdjudicator,
|
|
515
|
+
) -> ForecastCalibrationObservation:
|
|
516
|
+
"""Run the injected adjudicator and close all prediction/outcome joins."""
|
|
517
|
+
|
|
518
|
+
if type(prediction) is not ForecastPredictionReceipt:
|
|
519
|
+
raise TypeError("prediction must be exact ForecastPredictionReceipt")
|
|
520
|
+
prediction.revalidate()
|
|
521
|
+
if type(request) is not MeaningfulDirectionRequest:
|
|
522
|
+
raise TypeError("request must be exact MeaningfulDirectionRequest")
|
|
523
|
+
request.revalidate()
|
|
524
|
+
if not isinstance(adjudicator, MeaningfulMetricDirectionAdjudicator):
|
|
525
|
+
raise TypeError("adjudicator must implement the direction adjudicator port")
|
|
526
|
+
_require_token(adjudicator.policy_id, name="adjudicator.policy_id")
|
|
527
|
+
if type(adjudicator.policy_version) is not int or adjudicator.policy_version <= 0:
|
|
528
|
+
raise ValueError("adjudicator.policy_version must be positive")
|
|
529
|
+
require_sha256(adjudicator.definition_sha256, "adjudicator.definition_sha256")
|
|
530
|
+
receipt = adjudicator.adjudicate(request)
|
|
531
|
+
if type(receipt) is not MeaningfulDirectionAdjudicationReceipt:
|
|
532
|
+
raise TypeError("adjudicator returned a foreign receipt type")
|
|
533
|
+
receipt.require_request(request)
|
|
534
|
+
if (
|
|
535
|
+
receipt.adjudicator_policy_id != adjudicator.policy_id
|
|
536
|
+
or receipt.adjudicator_policy_version != adjudicator.policy_version
|
|
537
|
+
or receipt.adjudicator_definition_sha256 != adjudicator.definition_sha256
|
|
538
|
+
):
|
|
539
|
+
raise ValueError("adjudicator receipt uses a foreign policy identity")
|
|
540
|
+
return ForecastCalibrationObservation(prediction, receipt)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
@dataclass(frozen=True, slots=True)
|
|
544
|
+
class ForecastCalibrationCell:
|
|
545
|
+
"""One empirical/Beta-smoothed categorical correctness cell."""
|
|
546
|
+
|
|
547
|
+
metric_id: str
|
|
548
|
+
asserted_direction: MetricEffectDirection
|
|
549
|
+
confidence: ForecastConfidenceBin
|
|
550
|
+
family: str | None
|
|
551
|
+
observation_count: int
|
|
552
|
+
scorable_count: int
|
|
553
|
+
correct_count: int
|
|
554
|
+
prior: BetaCorrectnessPrior
|
|
555
|
+
|
|
556
|
+
def __post_init__(self) -> None:
|
|
557
|
+
_require_metric(self.metric_id)
|
|
558
|
+
if type(self.asserted_direction) is not MetricEffectDirection:
|
|
559
|
+
raise TypeError("asserted_direction must be exact MetricEffectDirection")
|
|
560
|
+
if type(self.confidence) is not ForecastConfidenceBin:
|
|
561
|
+
raise TypeError("confidence must be exact ForecastConfidenceBin")
|
|
562
|
+
if self.family is not None:
|
|
563
|
+
_require_token(self.family, name="family")
|
|
564
|
+
for name in ("observation_count", "scorable_count", "correct_count"):
|
|
565
|
+
if type(getattr(self, name)) is not int or getattr(self, name) < 0:
|
|
566
|
+
raise ValueError(f"{name} must be a non-negative exact integer")
|
|
567
|
+
if not self.correct_count <= self.scorable_count <= self.observation_count:
|
|
568
|
+
raise ValueError("calibration cell counts are inconsistent")
|
|
569
|
+
if type(self.prior) is not BetaCorrectnessPrior:
|
|
570
|
+
raise TypeError("prior must be exact BetaCorrectnessPrior")
|
|
571
|
+
self.prior.__post_init__()
|
|
572
|
+
if (
|
|
573
|
+
self.asserted_direction is MetricEffectDirection.UNKNOWN
|
|
574
|
+
and self.scorable_count != 0
|
|
575
|
+
):
|
|
576
|
+
raise ValueError("unknown-direction observations must be abstentions")
|
|
577
|
+
|
|
578
|
+
@property
|
|
579
|
+
def empirical_accuracy(self) -> float | None:
|
|
580
|
+
self.__post_init__()
|
|
581
|
+
if self.scorable_count == 0:
|
|
582
|
+
return None
|
|
583
|
+
return self.correct_count / self.scorable_count
|
|
584
|
+
|
|
585
|
+
@property
|
|
586
|
+
def posterior_correctness(self) -> float:
|
|
587
|
+
self.__post_init__()
|
|
588
|
+
return (self.prior.alpha + self.correct_count) / (
|
|
589
|
+
self.prior.alpha + self.prior.beta + self.scorable_count
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
def to_record(self) -> dict[str, object]:
|
|
593
|
+
self.__post_init__()
|
|
594
|
+
empirical = self.empirical_accuracy
|
|
595
|
+
return {
|
|
596
|
+
"metric_id": self.metric_id,
|
|
597
|
+
"asserted_direction": self.asserted_direction.value,
|
|
598
|
+
"confidence": self.confidence.value,
|
|
599
|
+
"family": self.family,
|
|
600
|
+
"observation_count": self.observation_count,
|
|
601
|
+
"scorable_count": self.scorable_count,
|
|
602
|
+
"correct_count": self.correct_count,
|
|
603
|
+
"empirical_accuracy_hex": (None if empirical is None else empirical.hex()),
|
|
604
|
+
"posterior_correctness_hex": self.posterior_correctness.hex(),
|
|
605
|
+
"prior": self.prior.to_record(),
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def _observation_key(
|
|
610
|
+
observation: ForecastCalibrationObservation,
|
|
611
|
+
) -> tuple[int, str, str, str]:
|
|
612
|
+
prediction = observation.prediction
|
|
613
|
+
return (
|
|
614
|
+
prediction.wave_index,
|
|
615
|
+
prediction.selector_decision_sha256,
|
|
616
|
+
prediction.option_id,
|
|
617
|
+
prediction.metric_id,
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def _cell_key(
|
|
622
|
+
cell: ForecastCalibrationCell,
|
|
623
|
+
) -> tuple[str, str, str, str]:
|
|
624
|
+
return (
|
|
625
|
+
cell.metric_id,
|
|
626
|
+
cell.asserted_direction.value,
|
|
627
|
+
cell.confidence.value,
|
|
628
|
+
"" if cell.family is None else cell.family,
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def _build_cell(
|
|
633
|
+
observations: tuple[ForecastCalibrationObservation, ...],
|
|
634
|
+
*,
|
|
635
|
+
metric_id: str,
|
|
636
|
+
direction: MetricEffectDirection,
|
|
637
|
+
confidence: ForecastConfidenceBin,
|
|
638
|
+
family: str | None,
|
|
639
|
+
prior: BetaCorrectnessPrior,
|
|
640
|
+
) -> ForecastCalibrationCell:
|
|
641
|
+
members = tuple(
|
|
642
|
+
observation
|
|
643
|
+
for observation in observations
|
|
644
|
+
if observation.prediction.metric_id == metric_id
|
|
645
|
+
and observation.prediction.asserted_direction is direction
|
|
646
|
+
and observation.prediction.confidence is confidence
|
|
647
|
+
and (family is None or observation.prediction.family == family)
|
|
648
|
+
)
|
|
649
|
+
scorable = tuple(value for value in members if value.correctness is not None)
|
|
650
|
+
return ForecastCalibrationCell(
|
|
651
|
+
metric_id=metric_id,
|
|
652
|
+
asserted_direction=direction,
|
|
653
|
+
confidence=confidence,
|
|
654
|
+
family=family,
|
|
655
|
+
observation_count=len(members),
|
|
656
|
+
scorable_count=len(scorable),
|
|
657
|
+
correct_count=sum(value.correctness is True for value in scorable),
|
|
658
|
+
prior=prior,
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
663
|
+
class ForecastCalibrationSnapshot:
|
|
664
|
+
"""Immutable calibration evidence available strictly before a wave."""
|
|
665
|
+
|
|
666
|
+
scope: ForecastCalibrationScope
|
|
667
|
+
cutoff_wave_index_exclusive: int
|
|
668
|
+
observations: tuple[ForecastCalibrationObservation, ...]
|
|
669
|
+
prior: BetaCorrectnessPrior = field(default_factory=BetaCorrectnessPrior)
|
|
670
|
+
family_min_support: int = 4
|
|
671
|
+
|
|
672
|
+
def __post_init__(self) -> None:
|
|
673
|
+
if type(self.scope) is not ForecastCalibrationScope:
|
|
674
|
+
raise TypeError("scope must be exact ForecastCalibrationScope")
|
|
675
|
+
self.scope.revalidate()
|
|
676
|
+
_require_wave(
|
|
677
|
+
self.cutoff_wave_index_exclusive,
|
|
678
|
+
name="cutoff_wave_index_exclusive",
|
|
679
|
+
)
|
|
680
|
+
if type(self.observations) is not tuple or any(
|
|
681
|
+
type(value) is not ForecastCalibrationObservation
|
|
682
|
+
for value in self.observations
|
|
683
|
+
):
|
|
684
|
+
raise TypeError("observations must contain exact calibration observations")
|
|
685
|
+
for value in self.observations:
|
|
686
|
+
value.revalidate()
|
|
687
|
+
if self.observations != tuple(sorted(self.observations, key=_observation_key)):
|
|
688
|
+
raise ValueError("observations must use canonical evidence order")
|
|
689
|
+
semantic_keys = tuple(_observation_key(value) for value in self.observations)
|
|
690
|
+
if len(set(semantic_keys)) != len(semantic_keys):
|
|
691
|
+
raise ValueError("snapshot contains duplicate forecast cells")
|
|
692
|
+
if any(value.prediction.scope != self.scope for value in self.observations):
|
|
693
|
+
raise ValueError("snapshot contains a foreign calibration scope")
|
|
694
|
+
if any(
|
|
695
|
+
value.prediction.wave_index >= self.cutoff_wave_index_exclusive
|
|
696
|
+
for value in self.observations
|
|
697
|
+
):
|
|
698
|
+
raise ValueError("snapshot contains current/future-wave outcome evidence")
|
|
699
|
+
if type(self.prior) is not BetaCorrectnessPrior:
|
|
700
|
+
raise TypeError("prior must be exact BetaCorrectnessPrior")
|
|
701
|
+
self.prior.__post_init__()
|
|
702
|
+
if type(self.family_min_support) is not int or self.family_min_support <= 0:
|
|
703
|
+
raise ValueError("family_min_support must be a positive exact integer")
|
|
704
|
+
|
|
705
|
+
def revalidate(self) -> None:
|
|
706
|
+
if type(self) is not ForecastCalibrationSnapshot:
|
|
707
|
+
raise TypeError("snapshot must be exact ForecastCalibrationSnapshot")
|
|
708
|
+
ForecastCalibrationSnapshot.__post_init__(self)
|
|
709
|
+
|
|
710
|
+
@property
|
|
711
|
+
def cells(self) -> tuple[ForecastCalibrationCell, ...]:
|
|
712
|
+
self.revalidate()
|
|
713
|
+
global_keys = sorted(
|
|
714
|
+
{
|
|
715
|
+
(
|
|
716
|
+
value.prediction.metric_id,
|
|
717
|
+
value.prediction.asserted_direction,
|
|
718
|
+
value.prediction.confidence,
|
|
719
|
+
)
|
|
720
|
+
for value in self.observations
|
|
721
|
+
},
|
|
722
|
+
key=lambda value: (value[0], value[1].value, value[2].value),
|
|
723
|
+
)
|
|
724
|
+
result = [
|
|
725
|
+
_build_cell(
|
|
726
|
+
self.observations,
|
|
727
|
+
metric_id=metric_id,
|
|
728
|
+
direction=direction,
|
|
729
|
+
confidence=confidence,
|
|
730
|
+
family=None,
|
|
731
|
+
prior=self.prior,
|
|
732
|
+
)
|
|
733
|
+
for metric_id, direction, confidence in global_keys
|
|
734
|
+
]
|
|
735
|
+
family_keys = sorted(
|
|
736
|
+
{
|
|
737
|
+
(
|
|
738
|
+
value.prediction.metric_id,
|
|
739
|
+
value.prediction.asserted_direction,
|
|
740
|
+
value.prediction.confidence,
|
|
741
|
+
value.prediction.family,
|
|
742
|
+
)
|
|
743
|
+
for value in self.observations
|
|
744
|
+
},
|
|
745
|
+
key=lambda value: (value[0], value[1].value, value[2].value, value[3]),
|
|
746
|
+
)
|
|
747
|
+
for metric_id, direction, confidence, family in family_keys:
|
|
748
|
+
cell = _build_cell(
|
|
749
|
+
self.observations,
|
|
750
|
+
metric_id=metric_id,
|
|
751
|
+
direction=direction,
|
|
752
|
+
confidence=confidence,
|
|
753
|
+
family=family,
|
|
754
|
+
prior=self.prior,
|
|
755
|
+
)
|
|
756
|
+
if cell.scorable_count >= self.family_min_support:
|
|
757
|
+
result.append(cell)
|
|
758
|
+
return tuple(sorted(result, key=_cell_key))
|
|
759
|
+
|
|
760
|
+
@property
|
|
761
|
+
def observation_count(self) -> int:
|
|
762
|
+
return len(self.observations)
|
|
763
|
+
|
|
764
|
+
@property
|
|
765
|
+
def abstention_count(self) -> int:
|
|
766
|
+
return sum(value.is_abstention for value in self.observations)
|
|
767
|
+
|
|
768
|
+
@property
|
|
769
|
+
def scorable_count(self) -> int:
|
|
770
|
+
return self.observation_count - self.abstention_count
|
|
771
|
+
|
|
772
|
+
@property
|
|
773
|
+
def correct_count(self) -> int:
|
|
774
|
+
return sum(value.correctness is True for value in self.observations)
|
|
775
|
+
|
|
776
|
+
@property
|
|
777
|
+
def empirical_accuracy(self) -> float | None:
|
|
778
|
+
if self.scorable_count == 0:
|
|
779
|
+
return None
|
|
780
|
+
return self.correct_count / self.scorable_count
|
|
781
|
+
|
|
782
|
+
def lookup(
|
|
783
|
+
self,
|
|
784
|
+
*,
|
|
785
|
+
metric_id: str,
|
|
786
|
+
asserted_direction: MetricEffectDirection,
|
|
787
|
+
confidence: ForecastConfidenceBin,
|
|
788
|
+
family: str,
|
|
789
|
+
) -> tuple[ForecastCalibrationCell, str]:
|
|
790
|
+
"""Use supported family evidence, then metric-global evidence, then prior."""
|
|
791
|
+
|
|
792
|
+
self.revalidate()
|
|
793
|
+
_require_metric(metric_id)
|
|
794
|
+
if type(asserted_direction) is not MetricEffectDirection:
|
|
795
|
+
raise TypeError("asserted_direction must be exact MetricEffectDirection")
|
|
796
|
+
if type(confidence) is not ForecastConfidenceBin:
|
|
797
|
+
raise TypeError("confidence must be exact ForecastConfidenceBin")
|
|
798
|
+
_require_token(family, name="family")
|
|
799
|
+
for cell in self.cells:
|
|
800
|
+
if (
|
|
801
|
+
cell.metric_id == metric_id
|
|
802
|
+
and cell.asserted_direction is asserted_direction
|
|
803
|
+
and cell.confidence is confidence
|
|
804
|
+
and cell.family == family
|
|
805
|
+
):
|
|
806
|
+
return cell, "supported_family"
|
|
807
|
+
for cell in self.cells:
|
|
808
|
+
if (
|
|
809
|
+
cell.metric_id == metric_id
|
|
810
|
+
and cell.asserted_direction is asserted_direction
|
|
811
|
+
and cell.confidence is confidence
|
|
812
|
+
and cell.family is None
|
|
813
|
+
):
|
|
814
|
+
return cell, "metric_direction_confidence"
|
|
815
|
+
return (
|
|
816
|
+
ForecastCalibrationCell(
|
|
817
|
+
metric_id=metric_id,
|
|
818
|
+
asserted_direction=asserted_direction,
|
|
819
|
+
confidence=confidence,
|
|
820
|
+
family=None,
|
|
821
|
+
observation_count=0,
|
|
822
|
+
scorable_count=0,
|
|
823
|
+
correct_count=0,
|
|
824
|
+
prior=self.prior,
|
|
825
|
+
),
|
|
826
|
+
"declared_prior",
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
830
|
+
self.revalidate()
|
|
831
|
+
accuracy = self.empirical_accuracy
|
|
832
|
+
return {
|
|
833
|
+
"schema_version": 1,
|
|
834
|
+
"scope": self.scope.to_record(),
|
|
835
|
+
"cutoff_wave_index_exclusive": self.cutoff_wave_index_exclusive,
|
|
836
|
+
"prior": self.prior.to_record(),
|
|
837
|
+
"family_min_support": self.family_min_support,
|
|
838
|
+
"observations": [value.to_record() for value in self.observations],
|
|
839
|
+
"cells": [value.to_record() for value in self.cells],
|
|
840
|
+
"summary": {
|
|
841
|
+
"observation_count": self.observation_count,
|
|
842
|
+
"abstention_count": self.abstention_count,
|
|
843
|
+
"scorable_count": self.scorable_count,
|
|
844
|
+
"correct_count": self.correct_count,
|
|
845
|
+
"empirical_accuracy_hex": (
|
|
846
|
+
None if accuracy is None else accuracy.hex()
|
|
847
|
+
),
|
|
848
|
+
},
|
|
849
|
+
"leakage_guard": "only_observation_wave_lt_exclusive_cutoff",
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
@property
|
|
853
|
+
def snapshot_sha256(self) -> str:
|
|
854
|
+
return _hash(_SNAPSHOT_DOMAIN, self._unsigned_record())
|
|
855
|
+
|
|
856
|
+
def to_record(self) -> dict[str, object]:
|
|
857
|
+
return {**self._unsigned_record(), "snapshot_sha256": self.snapshot_sha256}
|
|
858
|
+
|
|
859
|
+
def __eq__(self, other: object) -> bool:
|
|
860
|
+
return (
|
|
861
|
+
type(other) is ForecastCalibrationSnapshot
|
|
862
|
+
and self.snapshot_sha256 == other.snapshot_sha256
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
__hash__ = None
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
def build_calibration_snapshot(
|
|
869
|
+
observations: Sequence[ForecastCalibrationObservation],
|
|
870
|
+
*,
|
|
871
|
+
scope: ForecastCalibrationScope,
|
|
872
|
+
cutoff_wave_index_exclusive: int,
|
|
873
|
+
prior: BetaCorrectnessPrior = BetaCorrectnessPrior(),
|
|
874
|
+
family_min_support: int = 4,
|
|
875
|
+
) -> ForecastCalibrationSnapshot:
|
|
876
|
+
"""Filter an immutable multi-scope ledger at an exclusive wave cutoff."""
|
|
877
|
+
|
|
878
|
+
if isinstance(observations, (str, bytes)):
|
|
879
|
+
raise TypeError("observations must be a finite observation sequence")
|
|
880
|
+
if type(scope) is not ForecastCalibrationScope:
|
|
881
|
+
raise TypeError("scope must be exact ForecastCalibrationScope")
|
|
882
|
+
scope.revalidate()
|
|
883
|
+
_require_wave(
|
|
884
|
+
cutoff_wave_index_exclusive,
|
|
885
|
+
name="cutoff_wave_index_exclusive",
|
|
886
|
+
)
|
|
887
|
+
admitted: list[ForecastCalibrationObservation] = []
|
|
888
|
+
seen: set[str] = set()
|
|
889
|
+
for value in observations:
|
|
890
|
+
if type(value) is not ForecastCalibrationObservation:
|
|
891
|
+
raise TypeError("ledger contains a foreign observation type")
|
|
892
|
+
value.revalidate()
|
|
893
|
+
if value.observation_sha256 in seen:
|
|
894
|
+
raise ValueError("ledger contains a duplicate observation receipt")
|
|
895
|
+
seen.add(value.observation_sha256)
|
|
896
|
+
if (
|
|
897
|
+
value.prediction.scope == scope
|
|
898
|
+
and value.prediction.wave_index < cutoff_wave_index_exclusive
|
|
899
|
+
):
|
|
900
|
+
admitted.append(value)
|
|
901
|
+
return ForecastCalibrationSnapshot(
|
|
902
|
+
scope=scope,
|
|
903
|
+
cutoff_wave_index_exclusive=cutoff_wave_index_exclusive,
|
|
904
|
+
observations=tuple(sorted(admitted, key=_observation_key)),
|
|
905
|
+
prior=prior,
|
|
906
|
+
family_min_support=family_min_support,
|
|
907
|
+
)
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
__all__ = [
|
|
911
|
+
"BetaCorrectnessPrior",
|
|
912
|
+
"ForecastCalibrationObservation",
|
|
913
|
+
"ForecastCalibrationScope",
|
|
914
|
+
"ForecastCalibrationSnapshot",
|
|
915
|
+
"ForecastConfidenceBin",
|
|
916
|
+
"ForecastPredictionReceipt",
|
|
917
|
+
"MeaningfulDirectionAdjudicationReceipt",
|
|
918
|
+
"MeaningfulDirectionRequest",
|
|
919
|
+
"MeaningfulMetricDirectionAdjudicator",
|
|
920
|
+
"build_calibration_snapshot",
|
|
921
|
+
"observe_forecast",
|
|
922
|
+
]
|