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,1471 @@
|
|
|
1
|
+
"""Generic bounded partitioning and reassembly for all-action forecasts.
|
|
2
|
+
|
|
3
|
+
The application layer owns global coverage, bounded concurrency, deterministic
|
|
4
|
+
reassembly, and health diagnostics. Provider integrations only implement the
|
|
5
|
+
single-block port and never own option or metric identities.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import math
|
|
14
|
+
import re
|
|
15
|
+
from collections import Counter
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import Protocol, runtime_checkable
|
|
19
|
+
|
|
20
|
+
from agent_evolve.domain.ids import LLMCallId
|
|
21
|
+
from agent_evolve.domain.patch import require_sha256
|
|
22
|
+
from agent_evolve.ports.action_forecast import (
|
|
23
|
+
ActionEvidenceCitation,
|
|
24
|
+
ActionForecastBlockPolicy,
|
|
25
|
+
ActionForecastBlockRequest,
|
|
26
|
+
ActionForecastBlockResult,
|
|
27
|
+
ActionForecastBlockSpec,
|
|
28
|
+
ActionForecastDraft,
|
|
29
|
+
ActionForecastPartitionLayout,
|
|
30
|
+
ActionForecastPartitionPolicyBinding,
|
|
31
|
+
ActionForecastRequest,
|
|
32
|
+
ActionMetricForecast,
|
|
33
|
+
ResolvedActionForecast,
|
|
34
|
+
ResolvedActionForecastBatch,
|
|
35
|
+
ResolvedActionForecastBlock,
|
|
36
|
+
resolve_action_forecasts,
|
|
37
|
+
validate_action_forecast_partition_layout,
|
|
38
|
+
validate_resolved_action_forecast_block,
|
|
39
|
+
validate_resolved_action_forecasts,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
44
|
+
_BLOCK_CALL_ID_DOMAIN = b"agent-evolve:action-forecast-block-call-id:v1\x00"
|
|
45
|
+
_PARTITIONED_RESULT_DOMAIN = b"agent-evolve:partitioned-action-forecast-result:v1\x00"
|
|
46
|
+
_HEALTH_POLICY_DOMAIN = b"agent-evolve:action-forecast-health-policy:v1\x00"
|
|
47
|
+
_METRIC_HEALTH_DOMAIN = b"agent-evolve:action-forecast-metric-health:v2\x00"
|
|
48
|
+
_HEALTH_ASSESSMENT_DOMAIN = b"agent-evolve:action-forecast-health-assessment:v4\x00"
|
|
49
|
+
_HEALTH_SUBSET_POLICY_DOMAIN = (
|
|
50
|
+
b"agent-evolve:action-forecast-health-subset-policy:v1\x00"
|
|
51
|
+
)
|
|
52
|
+
_HEALTH_BLOCK_SUBSET_DOMAIN = b"agent-evolve:action-forecast-health-block-subset:v1\x00"
|
|
53
|
+
|
|
54
|
+
LENIENT_ACTION_FORECAST_HEALTH_POLICY_ID = "lenient_normalized_forecast_health"
|
|
55
|
+
LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_ID = (
|
|
56
|
+
LENIENT_ACTION_FORECAST_HEALTH_POLICY_ID
|
|
57
|
+
)
|
|
58
|
+
LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_VERSION = 2
|
|
59
|
+
LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
60
|
+
b"agent-evolve:lenient-normalized-forecast-health:v2;"
|
|
61
|
+
b"minimum_rows=8;extreme_abs_normalized_median=32;"
|
|
62
|
+
b"collapse_share_threshold=0.95;minimum_distinct_signatures=2"
|
|
63
|
+
).hexdigest()
|
|
64
|
+
LENIENT_ACTION_FORECAST_HEALTH_POLICY_VERSION = 3
|
|
65
|
+
LENIENT_ACTION_FORECAST_HEALTH_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
66
|
+
b"agent-evolve:lenient-normalized-forecast-health:v3;"
|
|
67
|
+
b"minimum_rows=8;extreme_abs_normalized_median=32;"
|
|
68
|
+
b"collapse_share_threshold=0.95;minimum_distinct_signatures=2;"
|
|
69
|
+
b"unit_confidence_share_below_collapse_threshold=true"
|
|
70
|
+
).hexdigest()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _canonical_json(value: object) -> bytes:
|
|
74
|
+
return json.dumps(
|
|
75
|
+
value,
|
|
76
|
+
allow_nan=False,
|
|
77
|
+
ensure_ascii=True,
|
|
78
|
+
separators=(",", ":"),
|
|
79
|
+
sort_keys=True,
|
|
80
|
+
).encode("ascii")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
84
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _finite_float(value: object, name: str) -> float:
|
|
88
|
+
if type(value) is not float or not math.isfinite(value):
|
|
89
|
+
raise TypeError(f"{name} must be a finite canonical float")
|
|
90
|
+
return value
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def build_action_forecast_partition_layout(
|
|
94
|
+
request: ActionForecastRequest,
|
|
95
|
+
partition_policy: ActionForecastPartitionPolicyBinding,
|
|
96
|
+
) -> ActionForecastPartitionLayout:
|
|
97
|
+
"""Create the unique maximal contiguous partition under two hard bounds."""
|
|
98
|
+
|
|
99
|
+
if type(request) is not ActionForecastRequest:
|
|
100
|
+
raise TypeError("request must be an exact ActionForecastRequest")
|
|
101
|
+
request.__post_init__()
|
|
102
|
+
if type(partition_policy) is not ActionForecastPartitionPolicyBinding:
|
|
103
|
+
raise TypeError(
|
|
104
|
+
"partition_policy must be an exact partition-policy binding"
|
|
105
|
+
)
|
|
106
|
+
partition_policy.__post_init__()
|
|
107
|
+
metric_count = len(request.required_metric_ids)
|
|
108
|
+
cell_bounded_rows = partition_policy.max_metric_cells_per_block // metric_count
|
|
109
|
+
rows_per_block = min(
|
|
110
|
+
partition_policy.max_rows_per_block,
|
|
111
|
+
cell_bounded_rows,
|
|
112
|
+
)
|
|
113
|
+
if rows_per_block < 1:
|
|
114
|
+
raise ValueError(
|
|
115
|
+
"max_metric_cells_per_block cannot hold one complete metric row"
|
|
116
|
+
)
|
|
117
|
+
options = request.finite_variation_contract.options
|
|
118
|
+
option_identities = tuple(option.identity_sha256 for option in options)
|
|
119
|
+
blocks = tuple(
|
|
120
|
+
ActionForecastBlockSpec(
|
|
121
|
+
block_index=block_index,
|
|
122
|
+
global_row_start=start,
|
|
123
|
+
global_row_stop=min(start + rows_per_block, len(options)),
|
|
124
|
+
option_identity_sha256s=option_identities[
|
|
125
|
+
start : min(start + rows_per_block, len(options))
|
|
126
|
+
],
|
|
127
|
+
)
|
|
128
|
+
for block_index, start in enumerate(range(0, len(options), rows_per_block))
|
|
129
|
+
)
|
|
130
|
+
layout = ActionForecastPartitionLayout(
|
|
131
|
+
finite_contract_identity_sha256=(
|
|
132
|
+
request.finite_variation_contract.identity_sha256
|
|
133
|
+
),
|
|
134
|
+
option_identity_sha256s=option_identities,
|
|
135
|
+
metric_ids=request.required_metric_ids,
|
|
136
|
+
partition_policy=partition_policy,
|
|
137
|
+
blocks=blocks,
|
|
138
|
+
)
|
|
139
|
+
validate_action_forecast_partition_layout(request, layout)
|
|
140
|
+
return layout
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def action_forecast_block_call_id(
|
|
144
|
+
request: ActionForecastRequest,
|
|
145
|
+
layout: ActionForecastPartitionLayout,
|
|
146
|
+
block: ActionForecastBlockSpec,
|
|
147
|
+
) -> LLMCallId:
|
|
148
|
+
"""Derive an opaque stable physical-call identity from sealed receipts."""
|
|
149
|
+
|
|
150
|
+
if type(request) is not ActionForecastRequest:
|
|
151
|
+
raise TypeError("request must be an exact ActionForecastRequest")
|
|
152
|
+
request.__post_init__()
|
|
153
|
+
validate_action_forecast_partition_layout(request, layout)
|
|
154
|
+
if type(block) is not ActionForecastBlockSpec:
|
|
155
|
+
raise TypeError("block must be an exact ActionForecastBlockSpec")
|
|
156
|
+
block.__post_init__()
|
|
157
|
+
if block.block_index >= layout.block_count or (
|
|
158
|
+
block != layout.blocks[block.block_index]
|
|
159
|
+
):
|
|
160
|
+
raise ValueError("block differs from its exact partition-layout position")
|
|
161
|
+
digest = _hash(
|
|
162
|
+
_BLOCK_CALL_ID_DOMAIN,
|
|
163
|
+
{
|
|
164
|
+
"request_sha256": request.request_sha256,
|
|
165
|
+
"layout_sha256": layout.layout_sha256,
|
|
166
|
+
"block_spec_sha256": block.block_spec_sha256,
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
return LLMCallId(
|
|
170
|
+
f"call_action_forecast_block_{block.block_index:06d}_{digest[:32]}"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def build_action_forecast_block_requests(
|
|
175
|
+
request: ActionForecastRequest,
|
|
176
|
+
layout: ActionForecastPartitionLayout,
|
|
177
|
+
) -> tuple[ActionForecastBlockRequest, ...]:
|
|
178
|
+
"""Materialize canonical block requests without dispatching provider work."""
|
|
179
|
+
|
|
180
|
+
validate_action_forecast_partition_layout(request, layout)
|
|
181
|
+
return tuple(
|
|
182
|
+
ActionForecastBlockRequest(
|
|
183
|
+
request=request,
|
|
184
|
+
layout=layout,
|
|
185
|
+
block=block,
|
|
186
|
+
block_call_id=action_forecast_block_call_id(request, layout, block),
|
|
187
|
+
)
|
|
188
|
+
for block in layout.blocks
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _draft_from_resolved(value: ResolvedActionForecast) -> ActionForecastDraft:
|
|
193
|
+
value.__post_init__()
|
|
194
|
+
return ActionForecastDraft(
|
|
195
|
+
option_id=value.option_id,
|
|
196
|
+
probability_valid=value.probability_valid,
|
|
197
|
+
metric_forecasts=tuple(
|
|
198
|
+
ActionMetricForecast(
|
|
199
|
+
metric_id=metric.metric_id,
|
|
200
|
+
p10_delta=metric.p10_delta,
|
|
201
|
+
p50_delta=metric.p50_delta,
|
|
202
|
+
p90_delta=metric.p90_delta,
|
|
203
|
+
confidence=metric.confidence,
|
|
204
|
+
citations=tuple(
|
|
205
|
+
ActionEvidenceCitation(
|
|
206
|
+
card_key=citation.card_key,
|
|
207
|
+
action_binding_identity_sha256=(
|
|
208
|
+
citation.action_binding_identity_sha256
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
for citation in metric.citations
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
for metric in value.metric_forecasts
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
220
|
+
class PartitionedActionForecastResult:
|
|
221
|
+
"""Complete batch plus canonical physical-block provenance.
|
|
222
|
+
|
|
223
|
+
Telemetry remains available on ``block_results`` but is deliberately not
|
|
224
|
+
part of this scientific forecast receipt. The receipt binds the resolved
|
|
225
|
+
content and every physical block receipt in canonical global order.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
request_sha256: str
|
|
229
|
+
layout: ActionForecastPartitionLayout
|
|
230
|
+
block_results: tuple[ActionForecastBlockResult, ...]
|
|
231
|
+
forecasts: ResolvedActionForecastBatch
|
|
232
|
+
|
|
233
|
+
def __post_init__(self) -> None:
|
|
234
|
+
require_sha256(self.request_sha256, "request_sha256")
|
|
235
|
+
if type(self.layout) is not ActionForecastPartitionLayout:
|
|
236
|
+
raise TypeError("layout must be an exact ActionForecastPartitionLayout")
|
|
237
|
+
self.layout.__post_init__()
|
|
238
|
+
if type(self.block_results) is not tuple or not self.block_results or any(
|
|
239
|
+
type(value) is not ActionForecastBlockResult
|
|
240
|
+
for value in self.block_results
|
|
241
|
+
):
|
|
242
|
+
raise ValueError(
|
|
243
|
+
"block_results must be a non-empty exact tuple of block results"
|
|
244
|
+
)
|
|
245
|
+
for value in self.block_results:
|
|
246
|
+
value.__post_init__()
|
|
247
|
+
indices = tuple(value.forecasts.block_index for value in self.block_results)
|
|
248
|
+
if indices != tuple(range(len(self.block_results))):
|
|
249
|
+
raise ValueError("block_results must use canonical complete block order")
|
|
250
|
+
if len(self.block_results) != self.layout.block_count:
|
|
251
|
+
raise ValueError("block_results must completely cover the layout")
|
|
252
|
+
if type(self.forecasts) is not ResolvedActionForecastBatch:
|
|
253
|
+
raise TypeError("forecasts must be an exact ResolvedActionForecastBatch")
|
|
254
|
+
self.forecasts.__post_init__()
|
|
255
|
+
if self.forecasts.request_sha256 != self.request_sha256:
|
|
256
|
+
raise ValueError("assembled forecasts differ from the partition request")
|
|
257
|
+
if (
|
|
258
|
+
self.forecasts.finite_contract_identity_sha256
|
|
259
|
+
!= self.layout.finite_contract_identity_sha256
|
|
260
|
+
):
|
|
261
|
+
raise ValueError("assembled forecasts differ from the partition contract")
|
|
262
|
+
policy_identity = (
|
|
263
|
+
self.forecasts.policy_id,
|
|
264
|
+
self.forecasts.policy_version,
|
|
265
|
+
self.forecasts.policy_definition_sha256,
|
|
266
|
+
)
|
|
267
|
+
for block_spec, result in zip(
|
|
268
|
+
self.layout.blocks,
|
|
269
|
+
self.block_results,
|
|
270
|
+
strict=True,
|
|
271
|
+
):
|
|
272
|
+
block = result.forecasts
|
|
273
|
+
if block.request_sha256 != self.request_sha256:
|
|
274
|
+
raise ValueError("a block differs from the partition request")
|
|
275
|
+
if block.layout_sha256 != self.layout.layout_sha256:
|
|
276
|
+
raise ValueError("a block differs from the partition layout")
|
|
277
|
+
if block.block_spec_sha256 != block_spec.block_spec_sha256:
|
|
278
|
+
raise ValueError("a block differs from its partition-layout position")
|
|
279
|
+
if (
|
|
280
|
+
block.policy_id,
|
|
281
|
+
block.policy_version,
|
|
282
|
+
block.policy_definition_sha256,
|
|
283
|
+
) != policy_identity:
|
|
284
|
+
raise ValueError("a block differs from the assembled policy")
|
|
285
|
+
flattened_ids = tuple(
|
|
286
|
+
forecast.option_id
|
|
287
|
+
for result in self.block_results
|
|
288
|
+
for forecast in result.forecasts.forecasts
|
|
289
|
+
)
|
|
290
|
+
if flattened_ids != tuple(
|
|
291
|
+
forecast.option_id for forecast in self.forecasts.forecasts
|
|
292
|
+
):
|
|
293
|
+
raise ValueError("assembled option order differs from canonical blocks")
|
|
294
|
+
for forecast in self.forecasts.forecasts:
|
|
295
|
+
if tuple(
|
|
296
|
+
metric.metric_id for metric in forecast.metric_forecasts
|
|
297
|
+
) != self.layout.metric_ids:
|
|
298
|
+
raise ValueError("assembled metrics differ from the partition layout")
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def layout_sha256(self) -> str:
|
|
302
|
+
return self.layout.layout_sha256
|
|
303
|
+
|
|
304
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
305
|
+
self.__post_init__()
|
|
306
|
+
return {
|
|
307
|
+
"schema_version": 1,
|
|
308
|
+
"request_sha256": self.request_sha256,
|
|
309
|
+
"layout_sha256": self.layout.layout_sha256,
|
|
310
|
+
"block_receipt_sha256s": [
|
|
311
|
+
value.forecasts.receipt_sha256 for value in self.block_results
|
|
312
|
+
],
|
|
313
|
+
"resolved_batch_receipt_sha256": self.forecasts.receipt_sha256,
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def receipt_sha256(self) -> str:
|
|
318
|
+
return _hash(_PARTITIONED_RESULT_DOMAIN, self._unsigned_record())
|
|
319
|
+
|
|
320
|
+
def to_record(self) -> dict[str, object]:
|
|
321
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
322
|
+
|
|
323
|
+
def __eq__(self, other: object) -> bool:
|
|
324
|
+
return (
|
|
325
|
+
type(self) is PartitionedActionForecastResult
|
|
326
|
+
and type(other) is PartitionedActionForecastResult
|
|
327
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
__hash__ = None
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def assemble_partitioned_action_forecasts(
|
|
334
|
+
request: ActionForecastRequest,
|
|
335
|
+
layout: ActionForecastPartitionLayout,
|
|
336
|
+
block_results: tuple[ActionForecastBlockResult, ...],
|
|
337
|
+
) -> PartitionedActionForecastResult:
|
|
338
|
+
"""Validate and deterministically reassemble all blocks or publish nothing."""
|
|
339
|
+
|
|
340
|
+
validate_action_forecast_partition_layout(request, layout)
|
|
341
|
+
if type(block_results) is not tuple or any(
|
|
342
|
+
type(value) is not ActionForecastBlockResult for value in block_results
|
|
343
|
+
):
|
|
344
|
+
raise TypeError(
|
|
345
|
+
"block_results must be an exact tuple of ActionForecastBlockResult"
|
|
346
|
+
)
|
|
347
|
+
if len(block_results) != layout.block_count:
|
|
348
|
+
raise ValueError("block results do not completely cover the partition layout")
|
|
349
|
+
for value in block_results:
|
|
350
|
+
value.__post_init__()
|
|
351
|
+
by_index: dict[int, ActionForecastBlockResult] = {}
|
|
352
|
+
for value in block_results:
|
|
353
|
+
index = value.forecasts.block_index
|
|
354
|
+
if index in by_index:
|
|
355
|
+
raise ValueError("block results repeat a partition block")
|
|
356
|
+
by_index[index] = value
|
|
357
|
+
if set(by_index) != set(range(layout.block_count)):
|
|
358
|
+
raise ValueError("block results contain a gap or foreign block index")
|
|
359
|
+
canonical = tuple(by_index[index] for index in range(layout.block_count))
|
|
360
|
+
expected_requests = build_action_forecast_block_requests(request, layout)
|
|
361
|
+
for block_request, result in zip(expected_requests, canonical, strict=True):
|
|
362
|
+
validate_resolved_action_forecast_block(
|
|
363
|
+
block_request,
|
|
364
|
+
result.forecasts,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
policy_identities = {
|
|
368
|
+
(
|
|
369
|
+
result.forecasts.policy_id,
|
|
370
|
+
result.forecasts.policy_version,
|
|
371
|
+
result.forecasts.policy_definition_sha256,
|
|
372
|
+
)
|
|
373
|
+
for result in canonical
|
|
374
|
+
}
|
|
375
|
+
if len(policy_identities) != 1:
|
|
376
|
+
raise ValueError("partition blocks contain policy drift")
|
|
377
|
+
policy_id, policy_version, policy_definition_sha256 = next(
|
|
378
|
+
iter(policy_identities)
|
|
379
|
+
)
|
|
380
|
+
drafts = tuple(
|
|
381
|
+
_draft_from_resolved(forecast)
|
|
382
|
+
for result in canonical
|
|
383
|
+
for forecast in result.forecasts.forecasts
|
|
384
|
+
)
|
|
385
|
+
batch = resolve_action_forecasts(
|
|
386
|
+
request,
|
|
387
|
+
drafts,
|
|
388
|
+
policy_id=policy_id,
|
|
389
|
+
policy_version=policy_version,
|
|
390
|
+
policy_definition_sha256=policy_definition_sha256,
|
|
391
|
+
)
|
|
392
|
+
validate_resolved_action_forecasts(request, batch)
|
|
393
|
+
return PartitionedActionForecastResult(
|
|
394
|
+
request_sha256=request.request_sha256,
|
|
395
|
+
layout=layout,
|
|
396
|
+
block_results=canonical,
|
|
397
|
+
forecasts=batch,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@dataclass(frozen=True, slots=True)
|
|
402
|
+
class ActionForecastBlockFailure:
|
|
403
|
+
block_index: int
|
|
404
|
+
block_request_sha256: str
|
|
405
|
+
error_type: str
|
|
406
|
+
error_message: str
|
|
407
|
+
|
|
408
|
+
def __post_init__(self) -> None:
|
|
409
|
+
if type(self.block_index) is not int or self.block_index < 0:
|
|
410
|
+
raise ValueError("block_index must be a non-negative exact integer")
|
|
411
|
+
require_sha256(self.block_request_sha256, "block_request_sha256")
|
|
412
|
+
if type(self.error_type) is not str or not self.error_type:
|
|
413
|
+
raise ValueError("error_type must be non-empty text")
|
|
414
|
+
if type(self.error_message) is not str:
|
|
415
|
+
raise TypeError("error_message must be exact text")
|
|
416
|
+
|
|
417
|
+
def to_record(self) -> dict[str, object]:
|
|
418
|
+
self.__post_init__()
|
|
419
|
+
return {
|
|
420
|
+
"block_index": self.block_index,
|
|
421
|
+
"block_request_sha256": self.block_request_sha256,
|
|
422
|
+
"error_type": self.error_type,
|
|
423
|
+
"error_message": self.error_message,
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
class ActionForecastWaveError(RuntimeError):
|
|
428
|
+
"""All blocks settled, but at least one failed and no batch was published."""
|
|
429
|
+
|
|
430
|
+
def __init__(
|
|
431
|
+
self,
|
|
432
|
+
*,
|
|
433
|
+
successful_results: tuple[ActionForecastBlockResult, ...],
|
|
434
|
+
failures: tuple[ActionForecastBlockFailure, ...],
|
|
435
|
+
) -> None:
|
|
436
|
+
if type(successful_results) is not tuple or any(
|
|
437
|
+
type(value) is not ActionForecastBlockResult
|
|
438
|
+
for value in successful_results
|
|
439
|
+
):
|
|
440
|
+
raise TypeError("successful_results must be an exact result tuple")
|
|
441
|
+
if type(failures) is not tuple or not failures or any(
|
|
442
|
+
type(value) is not ActionForecastBlockFailure for value in failures
|
|
443
|
+
):
|
|
444
|
+
raise ValueError("failures must be a non-empty exact failure tuple")
|
|
445
|
+
self.successful_results = successful_results
|
|
446
|
+
self.failures = failures
|
|
447
|
+
super().__init__(
|
|
448
|
+
f"{len(failures)} action-forecast block(s) failed after all settled"
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
@runtime_checkable
|
|
453
|
+
class PartitionedActionForecastPolicy(Protocol):
|
|
454
|
+
async def forecast_partitioned(
|
|
455
|
+
self,
|
|
456
|
+
request: ActionForecastRequest,
|
|
457
|
+
layout: ActionForecastPartitionLayout,
|
|
458
|
+
) -> PartitionedActionForecastResult: ...
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@dataclass(slots=True)
|
|
462
|
+
class ConcurrentActionForecastWave:
|
|
463
|
+
"""Queue-agnostic bounded worker coordinator for one partitioned request."""
|
|
464
|
+
|
|
465
|
+
block_policy: ActionForecastBlockPolicy
|
|
466
|
+
max_concurrency: int
|
|
467
|
+
|
|
468
|
+
def __post_init__(self) -> None:
|
|
469
|
+
if not callable(getattr(self.block_policy, "forecast_block", None)):
|
|
470
|
+
raise TypeError("block_policy must expose forecast_block")
|
|
471
|
+
if type(self.max_concurrency) is not int or self.max_concurrency <= 0:
|
|
472
|
+
raise ValueError("max_concurrency must be a positive exact integer")
|
|
473
|
+
|
|
474
|
+
async def forecast_partitioned(
|
|
475
|
+
self,
|
|
476
|
+
request: ActionForecastRequest,
|
|
477
|
+
layout: ActionForecastPartitionLayout,
|
|
478
|
+
) -> PartitionedActionForecastResult:
|
|
479
|
+
self.__post_init__()
|
|
480
|
+
block_requests = build_action_forecast_block_requests(request, layout)
|
|
481
|
+
results: list[ActionForecastBlockResult | None] = [
|
|
482
|
+
None for _ in block_requests
|
|
483
|
+
]
|
|
484
|
+
failures: list[ActionForecastBlockFailure] = []
|
|
485
|
+
next_position = 0
|
|
486
|
+
|
|
487
|
+
async def worker() -> None:
|
|
488
|
+
nonlocal next_position
|
|
489
|
+
while next_position < len(block_requests):
|
|
490
|
+
position = next_position
|
|
491
|
+
next_position += 1
|
|
492
|
+
block_request = block_requests[position]
|
|
493
|
+
try:
|
|
494
|
+
result = await self.block_policy.forecast_block(block_request)
|
|
495
|
+
if type(result) is not ActionForecastBlockResult:
|
|
496
|
+
raise TypeError(
|
|
497
|
+
"block_policy returned a non-ActionForecastBlockResult"
|
|
498
|
+
)
|
|
499
|
+
result.__post_init__()
|
|
500
|
+
validate_resolved_action_forecast_block(
|
|
501
|
+
block_request,
|
|
502
|
+
result.forecasts,
|
|
503
|
+
)
|
|
504
|
+
results[position] = result
|
|
505
|
+
except Exception as exc:
|
|
506
|
+
failures.append(
|
|
507
|
+
ActionForecastBlockFailure(
|
|
508
|
+
block_index=block_request.block.block_index,
|
|
509
|
+
block_request_sha256=(
|
|
510
|
+
block_request.block_request_sha256
|
|
511
|
+
),
|
|
512
|
+
error_type=(
|
|
513
|
+
f"{type(exc).__module__}.{type(exc).__qualname__}"
|
|
514
|
+
),
|
|
515
|
+
error_message=str(exc),
|
|
516
|
+
)
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
worker_count = min(self.max_concurrency, len(block_requests))
|
|
520
|
+
workers = tuple(asyncio.create_task(worker()) for _ in range(worker_count))
|
|
521
|
+
try:
|
|
522
|
+
await asyncio.gather(*workers)
|
|
523
|
+
except asyncio.CancelledError:
|
|
524
|
+
for task in workers:
|
|
525
|
+
if not task.done():
|
|
526
|
+
task.cancel()
|
|
527
|
+
await asyncio.gather(*workers, return_exceptions=True)
|
|
528
|
+
raise
|
|
529
|
+
|
|
530
|
+
successful = tuple(value for value in results if value is not None)
|
|
531
|
+
if failures:
|
|
532
|
+
raise ActionForecastWaveError(
|
|
533
|
+
successful_results=successful,
|
|
534
|
+
failures=tuple(sorted(failures, key=lambda value: value.block_index)),
|
|
535
|
+
)
|
|
536
|
+
return assemble_partitioned_action_forecasts(
|
|
537
|
+
request,
|
|
538
|
+
layout,
|
|
539
|
+
tuple(value for value in results if value is not None),
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
544
|
+
class ActionForecastHealthPolicyBinding:
|
|
545
|
+
"""Identified, benchmark-neutral normalized forecast health thresholds."""
|
|
546
|
+
|
|
547
|
+
policy_id: str
|
|
548
|
+
policy_version: int
|
|
549
|
+
policy_definition_sha256: str
|
|
550
|
+
minimum_rows: int
|
|
551
|
+
extreme_abs_normalized_median: float
|
|
552
|
+
collapse_share_threshold: float
|
|
553
|
+
minimum_distinct_signatures: int
|
|
554
|
+
|
|
555
|
+
def __post_init__(self) -> None:
|
|
556
|
+
if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
|
|
557
|
+
raise ValueError("policy_id must use the closed token grammar")
|
|
558
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
559
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
560
|
+
require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
|
|
561
|
+
if type(self.minimum_rows) is not int or self.minimum_rows <= 0:
|
|
562
|
+
raise ValueError("minimum_rows must be a positive exact integer")
|
|
563
|
+
_finite_float(
|
|
564
|
+
self.extreme_abs_normalized_median,
|
|
565
|
+
"extreme_abs_normalized_median",
|
|
566
|
+
)
|
|
567
|
+
if self.extreme_abs_normalized_median <= 0.0:
|
|
568
|
+
raise ValueError("extreme_abs_normalized_median must be positive")
|
|
569
|
+
_finite_float(self.collapse_share_threshold, "collapse_share_threshold")
|
|
570
|
+
if not 0.0 < self.collapse_share_threshold <= 1.0:
|
|
571
|
+
raise ValueError("collapse_share_threshold must lie in (0,1]")
|
|
572
|
+
if (
|
|
573
|
+
type(self.minimum_distinct_signatures) is not int
|
|
574
|
+
or self.minimum_distinct_signatures < 2
|
|
575
|
+
):
|
|
576
|
+
raise ValueError("minimum_distinct_signatures must be at least two")
|
|
577
|
+
|
|
578
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
579
|
+
self.__post_init__()
|
|
580
|
+
return {
|
|
581
|
+
"schema_version": 1,
|
|
582
|
+
"policy_id": self.policy_id,
|
|
583
|
+
"policy_version": self.policy_version,
|
|
584
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
585
|
+
"minimum_rows": self.minimum_rows,
|
|
586
|
+
"extreme_abs_normalized_median_hex": (
|
|
587
|
+
self.extreme_abs_normalized_median.hex()
|
|
588
|
+
),
|
|
589
|
+
"collapse_share_threshold_hex": self.collapse_share_threshold.hex(),
|
|
590
|
+
"minimum_distinct_signatures": self.minimum_distinct_signatures,
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
@property
|
|
594
|
+
def binding_sha256(self) -> str:
|
|
595
|
+
return _hash(_HEALTH_POLICY_DOMAIN, self._unsigned_record())
|
|
596
|
+
|
|
597
|
+
def to_record(self) -> dict[str, object]:
|
|
598
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
599
|
+
|
|
600
|
+
def __eq__(self, other: object) -> bool:
|
|
601
|
+
return (
|
|
602
|
+
type(self) is ActionForecastHealthPolicyBinding
|
|
603
|
+
and type(other) is ActionForecastHealthPolicyBinding
|
|
604
|
+
and self.binding_sha256 == other.binding_sha256
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
__hash__ = None
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def lenient_action_forecast_health_policy() -> ActionForecastHealthPolicyBinding:
|
|
611
|
+
return ActionForecastHealthPolicyBinding(
|
|
612
|
+
policy_id=LENIENT_ACTION_FORECAST_HEALTH_POLICY_ID,
|
|
613
|
+
policy_version=LENIENT_ACTION_FORECAST_HEALTH_POLICY_VERSION,
|
|
614
|
+
policy_definition_sha256=(
|
|
615
|
+
LENIENT_ACTION_FORECAST_HEALTH_POLICY_DEFINITION_SHA256
|
|
616
|
+
),
|
|
617
|
+
minimum_rows=8,
|
|
618
|
+
extreme_abs_normalized_median=32.0,
|
|
619
|
+
collapse_share_threshold=0.95,
|
|
620
|
+
minimum_distinct_signatures=2,
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def lenient_action_forecast_health_v2_policy() -> ActionForecastHealthPolicyBinding:
|
|
625
|
+
"""Reconstruct the frozen v2 policy binding for historical protocols.
|
|
626
|
+
|
|
627
|
+
New experiments must use :func:`lenient_action_forecast_health_policy`.
|
|
628
|
+
This named factory exists only so a sealed v2 protocol never silently
|
|
629
|
+
inherits later default-policy revisions.
|
|
630
|
+
"""
|
|
631
|
+
|
|
632
|
+
return ActionForecastHealthPolicyBinding(
|
|
633
|
+
policy_id=LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_ID,
|
|
634
|
+
policy_version=LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_VERSION,
|
|
635
|
+
policy_definition_sha256=(
|
|
636
|
+
LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_DEFINITION_SHA256
|
|
637
|
+
),
|
|
638
|
+
minimum_rows=8,
|
|
639
|
+
extreme_abs_normalized_median=32.0,
|
|
640
|
+
collapse_share_threshold=0.95,
|
|
641
|
+
minimum_distinct_signatures=2,
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
646
|
+
class ActionForecastMetricHealth:
|
|
647
|
+
metric_id: str
|
|
648
|
+
row_count: int
|
|
649
|
+
extreme_median_share: float
|
|
650
|
+
largest_median_bucket_share: float
|
|
651
|
+
zero_width_share: float
|
|
652
|
+
unit_confidence_share: float
|
|
653
|
+
distinct_cell_signature_count: int
|
|
654
|
+
distinct_confidence_count: int
|
|
655
|
+
max_abs_normalized_median: float
|
|
656
|
+
threshold_applied: bool
|
|
657
|
+
passes: bool
|
|
658
|
+
|
|
659
|
+
def __post_init__(self) -> None:
|
|
660
|
+
if type(self.metric_id) is not str or not self.metric_id:
|
|
661
|
+
raise ValueError("metric_id must be non-empty text")
|
|
662
|
+
if type(self.row_count) is not int or self.row_count <= 0:
|
|
663
|
+
raise ValueError("row_count must be a positive exact integer")
|
|
664
|
+
for name in (
|
|
665
|
+
"extreme_median_share",
|
|
666
|
+
"largest_median_bucket_share",
|
|
667
|
+
"zero_width_share",
|
|
668
|
+
"unit_confidence_share",
|
|
669
|
+
):
|
|
670
|
+
value = _finite_float(getattr(self, name), name)
|
|
671
|
+
if not 0.0 <= value <= 1.0:
|
|
672
|
+
raise ValueError(f"{name} must lie in [0,1]")
|
|
673
|
+
for name in (
|
|
674
|
+
"distinct_cell_signature_count",
|
|
675
|
+
"distinct_confidence_count",
|
|
676
|
+
):
|
|
677
|
+
value = getattr(self, name)
|
|
678
|
+
if type(value) is not int or value <= 0:
|
|
679
|
+
raise ValueError(f"{name} must be a positive exact integer")
|
|
680
|
+
_finite_float(
|
|
681
|
+
self.max_abs_normalized_median,
|
|
682
|
+
"max_abs_normalized_median",
|
|
683
|
+
)
|
|
684
|
+
if self.max_abs_normalized_median < 0.0:
|
|
685
|
+
raise ValueError("max_abs_normalized_median cannot be negative")
|
|
686
|
+
if type(self.threshold_applied) is not bool or type(self.passes) is not bool:
|
|
687
|
+
raise TypeError("threshold_applied and passes must be exact booleans")
|
|
688
|
+
|
|
689
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
690
|
+
self.__post_init__()
|
|
691
|
+
return {
|
|
692
|
+
"schema_version": 2,
|
|
693
|
+
"metric_id": self.metric_id,
|
|
694
|
+
"row_count": self.row_count,
|
|
695
|
+
"extreme_median_share_hex": self.extreme_median_share.hex(),
|
|
696
|
+
"largest_median_bucket_share_hex": (
|
|
697
|
+
self.largest_median_bucket_share.hex()
|
|
698
|
+
),
|
|
699
|
+
"zero_width_share_hex": self.zero_width_share.hex(),
|
|
700
|
+
"unit_confidence_share_hex": self.unit_confidence_share.hex(),
|
|
701
|
+
"distinct_cell_signature_count": self.distinct_cell_signature_count,
|
|
702
|
+
"distinct_confidence_count": self.distinct_confidence_count,
|
|
703
|
+
"max_abs_normalized_median_hex": (
|
|
704
|
+
self.max_abs_normalized_median.hex()
|
|
705
|
+
),
|
|
706
|
+
"threshold_applied": self.threshold_applied,
|
|
707
|
+
"passes": self.passes,
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
@property
|
|
711
|
+
def receipt_sha256(self) -> str:
|
|
712
|
+
return _hash(_METRIC_HEALTH_DOMAIN, self._unsigned_record())
|
|
713
|
+
|
|
714
|
+
def to_record(self) -> dict[str, object]:
|
|
715
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
716
|
+
|
|
717
|
+
def __eq__(self, other: object) -> bool:
|
|
718
|
+
return (
|
|
719
|
+
type(self) is ActionForecastMetricHealth
|
|
720
|
+
and type(other) is ActionForecastMetricHealth
|
|
721
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
__hash__ = None
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
728
|
+
class ActionForecastHealthSubsetPolicyBinding:
|
|
729
|
+
"""Identified scientific rule selecting rows from one validated block."""
|
|
730
|
+
|
|
731
|
+
policy_id: str
|
|
732
|
+
policy_version: int
|
|
733
|
+
policy_definition_sha256: str
|
|
734
|
+
|
|
735
|
+
def __post_init__(self) -> None:
|
|
736
|
+
if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
|
|
737
|
+
raise ValueError("policy_id must use the closed token grammar")
|
|
738
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
739
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
740
|
+
require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
|
|
741
|
+
|
|
742
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
743
|
+
self.__post_init__()
|
|
744
|
+
return {
|
|
745
|
+
"schema_version": 1,
|
|
746
|
+
"policy_id": self.policy_id,
|
|
747
|
+
"policy_version": self.policy_version,
|
|
748
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
@property
|
|
752
|
+
def binding_sha256(self) -> str:
|
|
753
|
+
return _hash(_HEALTH_SUBSET_POLICY_DOMAIN, self._unsigned_record())
|
|
754
|
+
|
|
755
|
+
def to_record(self) -> dict[str, object]:
|
|
756
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
757
|
+
|
|
758
|
+
def __eq__(self, other: object) -> bool:
|
|
759
|
+
return (
|
|
760
|
+
type(self) is ActionForecastHealthSubsetPolicyBinding
|
|
761
|
+
and type(other) is ActionForecastHealthSubsetPolicyBinding
|
|
762
|
+
and self.binding_sha256 == other.binding_sha256
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
__hash__ = None
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
769
|
+
class ActionForecastBlockHealthSubsetBinding:
|
|
770
|
+
"""Authenticated ordered row subset of one exact resolved partition block."""
|
|
771
|
+
|
|
772
|
+
subset_policy: ActionForecastHealthSubsetPolicyBinding
|
|
773
|
+
request_sha256: str
|
|
774
|
+
layout_sha256: str
|
|
775
|
+
block_request_sha256: str
|
|
776
|
+
block_spec_sha256: str
|
|
777
|
+
parent_block_receipt_sha256: str
|
|
778
|
+
block_index: int
|
|
779
|
+
global_row_start: int
|
|
780
|
+
global_row_stop: int
|
|
781
|
+
included_global_row_indices: tuple[int, ...]
|
|
782
|
+
included_option_identity_sha256s: tuple[str, ...]
|
|
783
|
+
|
|
784
|
+
def __post_init__(self) -> None:
|
|
785
|
+
if type(self.subset_policy) is not ActionForecastHealthSubsetPolicyBinding:
|
|
786
|
+
raise TypeError("subset_policy must be an exact subset-policy binding")
|
|
787
|
+
self.subset_policy.__post_init__()
|
|
788
|
+
for name in (
|
|
789
|
+
"request_sha256",
|
|
790
|
+
"layout_sha256",
|
|
791
|
+
"block_request_sha256",
|
|
792
|
+
"block_spec_sha256",
|
|
793
|
+
"parent_block_receipt_sha256",
|
|
794
|
+
):
|
|
795
|
+
require_sha256(getattr(self, name), name)
|
|
796
|
+
if type(self.block_index) is not int or self.block_index < 0:
|
|
797
|
+
raise ValueError("block_index must be a non-negative exact integer")
|
|
798
|
+
if type(self.global_row_start) is not int or self.global_row_start < 0:
|
|
799
|
+
raise ValueError("global_row_start must be a non-negative exact integer")
|
|
800
|
+
if (
|
|
801
|
+
type(self.global_row_stop) is not int
|
|
802
|
+
or self.global_row_stop <= self.global_row_start
|
|
803
|
+
):
|
|
804
|
+
raise ValueError("global_row_stop must be greater than global_row_start")
|
|
805
|
+
if type(self.included_global_row_indices) is not tuple or not (
|
|
806
|
+
self.included_global_row_indices
|
|
807
|
+
) or any(
|
|
808
|
+
type(value) is not int for value in self.included_global_row_indices
|
|
809
|
+
):
|
|
810
|
+
raise ValueError(
|
|
811
|
+
"included_global_row_indices must be a non-empty exact tuple"
|
|
812
|
+
)
|
|
813
|
+
if self.included_global_row_indices != tuple(
|
|
814
|
+
sorted(set(self.included_global_row_indices))
|
|
815
|
+
):
|
|
816
|
+
raise ValueError(
|
|
817
|
+
"included global rows must be unique and in block order"
|
|
818
|
+
)
|
|
819
|
+
if any(
|
|
820
|
+
value < self.global_row_start or value >= self.global_row_stop
|
|
821
|
+
for value in self.included_global_row_indices
|
|
822
|
+
):
|
|
823
|
+
raise ValueError("an included global row is outside the parent block")
|
|
824
|
+
if type(self.included_option_identity_sha256s) is not tuple or any(
|
|
825
|
+
type(value) is not str
|
|
826
|
+
for value in self.included_option_identity_sha256s
|
|
827
|
+
):
|
|
828
|
+
raise TypeError(
|
|
829
|
+
"included_option_identity_sha256s must be an exact tuple"
|
|
830
|
+
)
|
|
831
|
+
if len(self.included_option_identity_sha256s) != len(
|
|
832
|
+
self.included_global_row_indices
|
|
833
|
+
):
|
|
834
|
+
raise ValueError("included row and option identity counts must match")
|
|
835
|
+
for index, value in enumerate(self.included_option_identity_sha256s):
|
|
836
|
+
require_sha256(value, f"included_option_identity_sha256s[{index}]")
|
|
837
|
+
if len(set(self.included_option_identity_sha256s)) != len(
|
|
838
|
+
self.included_option_identity_sha256s
|
|
839
|
+
):
|
|
840
|
+
raise ValueError("an authenticated subset cannot repeat an option")
|
|
841
|
+
|
|
842
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
843
|
+
self.__post_init__()
|
|
844
|
+
return {
|
|
845
|
+
"schema_version": 1,
|
|
846
|
+
"subset_policy": self.subset_policy.to_record(),
|
|
847
|
+
"request_sha256": self.request_sha256,
|
|
848
|
+
"layout_sha256": self.layout_sha256,
|
|
849
|
+
"block_request_sha256": self.block_request_sha256,
|
|
850
|
+
"block_spec_sha256": self.block_spec_sha256,
|
|
851
|
+
"parent_block_receipt_sha256": self.parent_block_receipt_sha256,
|
|
852
|
+
"block_index": self.block_index,
|
|
853
|
+
"global_row_start": self.global_row_start,
|
|
854
|
+
"global_row_stop": self.global_row_stop,
|
|
855
|
+
"included_global_row_indices": list(
|
|
856
|
+
self.included_global_row_indices
|
|
857
|
+
),
|
|
858
|
+
"included_option_identity_sha256s": list(
|
|
859
|
+
self.included_option_identity_sha256s
|
|
860
|
+
),
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
@property
|
|
864
|
+
def binding_sha256(self) -> str:
|
|
865
|
+
return _hash(_HEALTH_BLOCK_SUBSET_DOMAIN, self._unsigned_record())
|
|
866
|
+
|
|
867
|
+
def to_record(self) -> dict[str, object]:
|
|
868
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
869
|
+
|
|
870
|
+
def __eq__(self, other: object) -> bool:
|
|
871
|
+
return (
|
|
872
|
+
type(self) is ActionForecastBlockHealthSubsetBinding
|
|
873
|
+
and type(other) is ActionForecastBlockHealthSubsetBinding
|
|
874
|
+
and self.binding_sha256 == other.binding_sha256
|
|
875
|
+
)
|
|
876
|
+
|
|
877
|
+
__hash__ = None
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
class ActionForecastHealthFrameKind(str, Enum):
|
|
881
|
+
COMPLETE = "complete"
|
|
882
|
+
PARTITION_BLOCK = "partition_block"
|
|
883
|
+
PARTITION_BLOCK_SUBSET = "partition_block_subset"
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
@dataclass(frozen=True, slots=True)
|
|
887
|
+
class _NormalizedActionForecastHealth:
|
|
888
|
+
metric_assessments: tuple[ActionForecastMetricHealth, ...]
|
|
889
|
+
distinct_row_signature_count: int
|
|
890
|
+
distinct_probability_valid_count: int
|
|
891
|
+
constant_confidence_metric_ids: tuple[str, ...]
|
|
892
|
+
threshold_applied: bool
|
|
893
|
+
passes: bool
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
897
|
+
class ResolvedActionForecastHealthAssessment:
|
|
898
|
+
member_id: str
|
|
899
|
+
frame_kind: ActionForecastHealthFrameKind
|
|
900
|
+
frame_receipt_sha256: str
|
|
901
|
+
request_sha256: str
|
|
902
|
+
layout_sha256: str | None
|
|
903
|
+
block_request_sha256: str | None
|
|
904
|
+
block_spec_sha256: str | None
|
|
905
|
+
block_index: int | None
|
|
906
|
+
global_row_start: int
|
|
907
|
+
global_row_stop: int
|
|
908
|
+
subset_binding: ActionForecastBlockHealthSubsetBinding | None
|
|
909
|
+
health_policy: ActionForecastHealthPolicyBinding
|
|
910
|
+
metric_assessments: tuple[ActionForecastMetricHealth, ...]
|
|
911
|
+
distinct_row_signature_count: int
|
|
912
|
+
distinct_probability_valid_count: int
|
|
913
|
+
constant_confidence_metric_ids: tuple[str, ...]
|
|
914
|
+
threshold_applied: bool
|
|
915
|
+
passes: bool
|
|
916
|
+
|
|
917
|
+
def __post_init__(self) -> None:
|
|
918
|
+
if type(self.member_id) is not str or _TOKEN.fullmatch(self.member_id) is None:
|
|
919
|
+
raise ValueError("member_id must use the closed token grammar")
|
|
920
|
+
if type(self.frame_kind) is not ActionForecastHealthFrameKind:
|
|
921
|
+
raise TypeError("frame_kind must be an exact health-frame kind")
|
|
922
|
+
require_sha256(self.frame_receipt_sha256, "frame_receipt_sha256")
|
|
923
|
+
require_sha256(self.request_sha256, "request_sha256")
|
|
924
|
+
if type(self.global_row_start) is not int or self.global_row_start < 0:
|
|
925
|
+
raise ValueError("global_row_start must be a non-negative exact integer")
|
|
926
|
+
if (
|
|
927
|
+
type(self.global_row_stop) is not int
|
|
928
|
+
or self.global_row_stop <= self.global_row_start
|
|
929
|
+
):
|
|
930
|
+
raise ValueError("global_row_stop must be greater than global_row_start")
|
|
931
|
+
if self.frame_kind is ActionForecastHealthFrameKind.COMPLETE:
|
|
932
|
+
if self.global_row_start != 0:
|
|
933
|
+
raise ValueError("complete health frames must start at global row zero")
|
|
934
|
+
if any(
|
|
935
|
+
value is not None
|
|
936
|
+
for value in (
|
|
937
|
+
self.layout_sha256,
|
|
938
|
+
self.block_request_sha256,
|
|
939
|
+
self.block_spec_sha256,
|
|
940
|
+
self.block_index,
|
|
941
|
+
)
|
|
942
|
+
):
|
|
943
|
+
raise ValueError("complete health frames forbid block identities")
|
|
944
|
+
if self.subset_binding is not None:
|
|
945
|
+
raise ValueError("complete health frames forbid subset bindings")
|
|
946
|
+
else:
|
|
947
|
+
for name in (
|
|
948
|
+
"layout_sha256",
|
|
949
|
+
"block_request_sha256",
|
|
950
|
+
"block_spec_sha256",
|
|
951
|
+
):
|
|
952
|
+
value = getattr(self, name)
|
|
953
|
+
if type(value) is not str:
|
|
954
|
+
raise TypeError(f"partition-block health requires {name}")
|
|
955
|
+
require_sha256(value, name)
|
|
956
|
+
if type(self.block_index) is not int or self.block_index < 0:
|
|
957
|
+
raise ValueError(
|
|
958
|
+
"partition-block health requires a non-negative block_index"
|
|
959
|
+
)
|
|
960
|
+
if (
|
|
961
|
+
self.frame_kind
|
|
962
|
+
is ActionForecastHealthFrameKind.PARTITION_BLOCK
|
|
963
|
+
):
|
|
964
|
+
if self.subset_binding is not None:
|
|
965
|
+
raise ValueError(
|
|
966
|
+
"full partition-block health forbids subset bindings"
|
|
967
|
+
)
|
|
968
|
+
else:
|
|
969
|
+
if type(self.subset_binding) is not (
|
|
970
|
+
ActionForecastBlockHealthSubsetBinding
|
|
971
|
+
):
|
|
972
|
+
raise TypeError(
|
|
973
|
+
"partition-block-subset health requires a subset binding"
|
|
974
|
+
)
|
|
975
|
+
self.subset_binding.__post_init__()
|
|
976
|
+
if (
|
|
977
|
+
self.frame_receipt_sha256
|
|
978
|
+
!= self.subset_binding.binding_sha256
|
|
979
|
+
or self.request_sha256 != self.subset_binding.request_sha256
|
|
980
|
+
or self.layout_sha256 != self.subset_binding.layout_sha256
|
|
981
|
+
or self.block_request_sha256
|
|
982
|
+
!= self.subset_binding.block_request_sha256
|
|
983
|
+
or self.block_spec_sha256
|
|
984
|
+
!= self.subset_binding.block_spec_sha256
|
|
985
|
+
or self.block_index != self.subset_binding.block_index
|
|
986
|
+
or self.global_row_start
|
|
987
|
+
!= self.subset_binding.global_row_start
|
|
988
|
+
or self.global_row_stop != self.subset_binding.global_row_stop
|
|
989
|
+
):
|
|
990
|
+
raise ValueError(
|
|
991
|
+
"subset health frame differs from its authenticated binding"
|
|
992
|
+
)
|
|
993
|
+
if type(self.health_policy) is not ActionForecastHealthPolicyBinding:
|
|
994
|
+
raise TypeError("health_policy must be an exact health-policy binding")
|
|
995
|
+
self.health_policy.__post_init__()
|
|
996
|
+
if type(self.metric_assessments) is not tuple or not (
|
|
997
|
+
self.metric_assessments
|
|
998
|
+
) or any(
|
|
999
|
+
type(value) is not ActionForecastMetricHealth
|
|
1000
|
+
for value in self.metric_assessments
|
|
1001
|
+
):
|
|
1002
|
+
raise ValueError("metric_assessments must be a non-empty exact tuple")
|
|
1003
|
+
for value in self.metric_assessments:
|
|
1004
|
+
value.__post_init__()
|
|
1005
|
+
metric_ids = tuple(value.metric_id for value in self.metric_assessments)
|
|
1006
|
+
if metric_ids != tuple(sorted(set(metric_ids))):
|
|
1007
|
+
raise ValueError("metric assessments must use canonical unique order")
|
|
1008
|
+
row_count = (
|
|
1009
|
+
len(self.subset_binding.included_global_row_indices)
|
|
1010
|
+
if self.frame_kind
|
|
1011
|
+
is ActionForecastHealthFrameKind.PARTITION_BLOCK_SUBSET
|
|
1012
|
+
and self.subset_binding is not None
|
|
1013
|
+
else self.global_row_stop - self.global_row_start
|
|
1014
|
+
)
|
|
1015
|
+
if any(value.row_count != row_count for value in self.metric_assessments):
|
|
1016
|
+
raise ValueError("metric health row counts differ from the bound frame")
|
|
1017
|
+
for name in (
|
|
1018
|
+
"distinct_row_signature_count",
|
|
1019
|
+
"distinct_probability_valid_count",
|
|
1020
|
+
):
|
|
1021
|
+
value = getattr(self, name)
|
|
1022
|
+
if type(value) is not int or value <= 0:
|
|
1023
|
+
raise ValueError(f"{name} must be a positive exact integer")
|
|
1024
|
+
if value > row_count:
|
|
1025
|
+
raise ValueError(f"{name} cannot exceed the bound frame row count")
|
|
1026
|
+
if type(self.constant_confidence_metric_ids) is not tuple or any(
|
|
1027
|
+
type(value) is not str for value in self.constant_confidence_metric_ids
|
|
1028
|
+
):
|
|
1029
|
+
raise TypeError("constant_confidence_metric_ids must be an exact tuple")
|
|
1030
|
+
if self.constant_confidence_metric_ids != tuple(
|
|
1031
|
+
sorted(set(self.constant_confidence_metric_ids))
|
|
1032
|
+
):
|
|
1033
|
+
raise ValueError(
|
|
1034
|
+
"constant_confidence_metric_ids must be unique and canonical"
|
|
1035
|
+
)
|
|
1036
|
+
if not set(self.constant_confidence_metric_ids).issubset(metric_ids):
|
|
1037
|
+
raise ValueError("constant-confidence metrics escape the assessment")
|
|
1038
|
+
if type(self.threshold_applied) is not bool or type(self.passes) is not bool:
|
|
1039
|
+
raise TypeError("threshold_applied and passes must be exact booleans")
|
|
1040
|
+
expected_threshold_applied = row_count >= self.health_policy.minimum_rows
|
|
1041
|
+
if self.threshold_applied is not expected_threshold_applied:
|
|
1042
|
+
raise ValueError("threshold_applied differs from the bound frame size")
|
|
1043
|
+
if any(
|
|
1044
|
+
value.threshold_applied is not self.threshold_applied
|
|
1045
|
+
for value in self.metric_assessments
|
|
1046
|
+
):
|
|
1047
|
+
raise ValueError("metric health threshold application is inconsistent")
|
|
1048
|
+
rows_pass = not self.threshold_applied or (
|
|
1049
|
+
self.distinct_row_signature_count
|
|
1050
|
+
>= self.health_policy.minimum_distinct_signatures
|
|
1051
|
+
)
|
|
1052
|
+
expected_passes = rows_pass and all(
|
|
1053
|
+
value.passes for value in self.metric_assessments
|
|
1054
|
+
)
|
|
1055
|
+
if self.passes is not expected_passes:
|
|
1056
|
+
raise ValueError("passes differs from the normalized health gates")
|
|
1057
|
+
|
|
1058
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1059
|
+
self.__post_init__()
|
|
1060
|
+
return {
|
|
1061
|
+
"schema_version": 4,
|
|
1062
|
+
"member_id": self.member_id,
|
|
1063
|
+
"frame_kind": self.frame_kind.value,
|
|
1064
|
+
"frame_receipt_sha256": self.frame_receipt_sha256,
|
|
1065
|
+
"request_sha256": self.request_sha256,
|
|
1066
|
+
"layout_sha256": self.layout_sha256,
|
|
1067
|
+
"block_request_sha256": self.block_request_sha256,
|
|
1068
|
+
"block_spec_sha256": self.block_spec_sha256,
|
|
1069
|
+
"block_index": self.block_index,
|
|
1070
|
+
"global_row_start": self.global_row_start,
|
|
1071
|
+
"global_row_stop": self.global_row_stop,
|
|
1072
|
+
"subset_binding": (
|
|
1073
|
+
None
|
|
1074
|
+
if self.subset_binding is None
|
|
1075
|
+
else self.subset_binding.to_record()
|
|
1076
|
+
),
|
|
1077
|
+
"health_policy": self.health_policy.to_record(),
|
|
1078
|
+
"metric_assessments": [
|
|
1079
|
+
value.to_record() for value in self.metric_assessments
|
|
1080
|
+
],
|
|
1081
|
+
"distinct_row_signature_count": self.distinct_row_signature_count,
|
|
1082
|
+
"distinct_probability_valid_count": (
|
|
1083
|
+
self.distinct_probability_valid_count
|
|
1084
|
+
),
|
|
1085
|
+
"constant_confidence_metric_ids": list(
|
|
1086
|
+
self.constant_confidence_metric_ids
|
|
1087
|
+
),
|
|
1088
|
+
"threshold_applied": self.threshold_applied,
|
|
1089
|
+
"passes": self.passes,
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
@property
|
|
1093
|
+
def receipt_sha256(self) -> str:
|
|
1094
|
+
return _hash(_HEALTH_ASSESSMENT_DOMAIN, self._unsigned_record())
|
|
1095
|
+
|
|
1096
|
+
def to_record(self) -> dict[str, object]:
|
|
1097
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
1098
|
+
|
|
1099
|
+
def __eq__(self, other: object) -> bool:
|
|
1100
|
+
return (
|
|
1101
|
+
type(self) is ResolvedActionForecastHealthAssessment
|
|
1102
|
+
and type(other) is ResolvedActionForecastHealthAssessment
|
|
1103
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
__hash__ = None
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
def _normalized(value: float, scale: float) -> float:
|
|
1110
|
+
result = value / scale
|
|
1111
|
+
if not math.isfinite(result):
|
|
1112
|
+
raise ValueError("normalized forecast component must remain finite")
|
|
1113
|
+
return 0.0 if result == 0.0 else result
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
def _validate_health_inputs(
|
|
1117
|
+
*,
|
|
1118
|
+
member_id: str,
|
|
1119
|
+
health_policy: ActionForecastHealthPolicyBinding,
|
|
1120
|
+
) -> None:
|
|
1121
|
+
if type(member_id) is not str or _TOKEN.fullmatch(member_id) is None:
|
|
1122
|
+
raise ValueError("member_id must use the closed token grammar")
|
|
1123
|
+
if type(health_policy) is not ActionForecastHealthPolicyBinding:
|
|
1124
|
+
raise TypeError("health_policy must be an exact health-policy binding")
|
|
1125
|
+
health_policy.__post_init__()
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
def _assess_normalized_forecasts(
|
|
1129
|
+
forecasts: tuple[ResolvedActionForecast, ...],
|
|
1130
|
+
request: ActionForecastRequest,
|
|
1131
|
+
health_policy: ActionForecastHealthPolicyBinding,
|
|
1132
|
+
) -> _NormalizedActionForecastHealth:
|
|
1133
|
+
"""Apply one normalized gate implementation to any validated row frame."""
|
|
1134
|
+
|
|
1135
|
+
if type(forecasts) is not tuple or not forecasts or any(
|
|
1136
|
+
type(value) is not ResolvedActionForecast for value in forecasts
|
|
1137
|
+
):
|
|
1138
|
+
raise ValueError("forecasts must be a non-empty exact resolved tuple")
|
|
1139
|
+
for value in forecasts:
|
|
1140
|
+
value.__post_init__()
|
|
1141
|
+
row_count = len(forecasts)
|
|
1142
|
+
threshold_applied = row_count >= health_policy.minimum_rows
|
|
1143
|
+
metric_assessments: list[ActionForecastMetricHealth] = []
|
|
1144
|
+
constant_confidence_metric_ids: list[str] = []
|
|
1145
|
+
row_signatures: set[tuple[object, ...]] = set()
|
|
1146
|
+
probability_buckets: set[str] = set()
|
|
1147
|
+
|
|
1148
|
+
for forecast in forecasts:
|
|
1149
|
+
probability_buckets.add(forecast.probability_valid.hex())
|
|
1150
|
+
row_signature: list[object] = [forecast.probability_valid.hex()]
|
|
1151
|
+
for metric, scale in zip(
|
|
1152
|
+
forecast.metric_forecasts,
|
|
1153
|
+
request.metric_scales,
|
|
1154
|
+
strict=True,
|
|
1155
|
+
):
|
|
1156
|
+
median = _normalized(metric.p50_delta, scale.delta_scale)
|
|
1157
|
+
lower = _normalized(
|
|
1158
|
+
metric.p50_delta - metric.p10_delta,
|
|
1159
|
+
scale.delta_scale,
|
|
1160
|
+
)
|
|
1161
|
+
upper = _normalized(
|
|
1162
|
+
metric.p90_delta - metric.p50_delta,
|
|
1163
|
+
scale.delta_scale,
|
|
1164
|
+
)
|
|
1165
|
+
row_signature.append(
|
|
1166
|
+
(median.hex(), lower.hex(), upper.hex(), metric.confidence.hex())
|
|
1167
|
+
)
|
|
1168
|
+
row_signatures.add(tuple(row_signature))
|
|
1169
|
+
|
|
1170
|
+
for metric_index, scale in enumerate(request.metric_scales):
|
|
1171
|
+
median_buckets: Counter[str] = Counter()
|
|
1172
|
+
cell_signatures: set[tuple[str, str, str, str]] = set()
|
|
1173
|
+
confidence_buckets: set[str] = set()
|
|
1174
|
+
extreme_count = 0
|
|
1175
|
+
zero_width_count = 0
|
|
1176
|
+
unit_confidence_count = 0
|
|
1177
|
+
max_abs_median = 0.0
|
|
1178
|
+
for forecast in forecasts:
|
|
1179
|
+
metric = forecast.metric_forecasts[metric_index]
|
|
1180
|
+
median = _normalized(metric.p50_delta, scale.delta_scale)
|
|
1181
|
+
lower = _normalized(
|
|
1182
|
+
metric.p50_delta - metric.p10_delta,
|
|
1183
|
+
scale.delta_scale,
|
|
1184
|
+
)
|
|
1185
|
+
upper = _normalized(
|
|
1186
|
+
metric.p90_delta - metric.p50_delta,
|
|
1187
|
+
scale.delta_scale,
|
|
1188
|
+
)
|
|
1189
|
+
median_buckets[median.hex()] += 1
|
|
1190
|
+
confidence_buckets.add(metric.confidence.hex())
|
|
1191
|
+
cell_signatures.add(
|
|
1192
|
+
(median.hex(), lower.hex(), upper.hex(), metric.confidence.hex())
|
|
1193
|
+
)
|
|
1194
|
+
if abs(median) >= health_policy.extreme_abs_normalized_median:
|
|
1195
|
+
extreme_count += 1
|
|
1196
|
+
if lower == 0.0 and upper == 0.0:
|
|
1197
|
+
zero_width_count += 1
|
|
1198
|
+
if metric.confidence == 1.0:
|
|
1199
|
+
unit_confidence_count += 1
|
|
1200
|
+
max_abs_median = max(max_abs_median, abs(median))
|
|
1201
|
+
|
|
1202
|
+
extreme_share = extreme_count / row_count
|
|
1203
|
+
largest_bucket_share = max(median_buckets.values()) / row_count
|
|
1204
|
+
zero_width_share = zero_width_count / row_count
|
|
1205
|
+
unit_confidence_share = unit_confidence_count / row_count
|
|
1206
|
+
passes = not threshold_applied or (
|
|
1207
|
+
extreme_share < health_policy.collapse_share_threshold
|
|
1208
|
+
and largest_bucket_share < health_policy.collapse_share_threshold
|
|
1209
|
+
and zero_width_share < health_policy.collapse_share_threshold
|
|
1210
|
+
and unit_confidence_share < health_policy.collapse_share_threshold
|
|
1211
|
+
and len(cell_signatures)
|
|
1212
|
+
>= health_policy.minimum_distinct_signatures
|
|
1213
|
+
)
|
|
1214
|
+
metric_assessments.append(
|
|
1215
|
+
ActionForecastMetricHealth(
|
|
1216
|
+
metric_id=scale.metric_id,
|
|
1217
|
+
row_count=row_count,
|
|
1218
|
+
extreme_median_share=float(extreme_share),
|
|
1219
|
+
largest_median_bucket_share=float(largest_bucket_share),
|
|
1220
|
+
zero_width_share=float(zero_width_share),
|
|
1221
|
+
unit_confidence_share=float(unit_confidence_share),
|
|
1222
|
+
distinct_cell_signature_count=len(cell_signatures),
|
|
1223
|
+
distinct_confidence_count=len(confidence_buckets),
|
|
1224
|
+
max_abs_normalized_median=float(max_abs_median),
|
|
1225
|
+
threshold_applied=threshold_applied,
|
|
1226
|
+
passes=passes,
|
|
1227
|
+
)
|
|
1228
|
+
)
|
|
1229
|
+
if len(confidence_buckets) == 1:
|
|
1230
|
+
constant_confidence_metric_ids.append(scale.metric_id)
|
|
1231
|
+
|
|
1232
|
+
rows_pass = not threshold_applied or (
|
|
1233
|
+
len(row_signatures) >= health_policy.minimum_distinct_signatures
|
|
1234
|
+
)
|
|
1235
|
+
assessments = tuple(metric_assessments)
|
|
1236
|
+
return _NormalizedActionForecastHealth(
|
|
1237
|
+
metric_assessments=assessments,
|
|
1238
|
+
distinct_row_signature_count=len(row_signatures),
|
|
1239
|
+
distinct_probability_valid_count=len(probability_buckets),
|
|
1240
|
+
constant_confidence_metric_ids=tuple(
|
|
1241
|
+
sorted(constant_confidence_metric_ids)
|
|
1242
|
+
),
|
|
1243
|
+
threshold_applied=threshold_applied,
|
|
1244
|
+
passes=rows_pass and all(value.passes for value in assessments),
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
def _bind_health_assessment(
|
|
1249
|
+
*,
|
|
1250
|
+
member_id: str,
|
|
1251
|
+
frame_kind: ActionForecastHealthFrameKind,
|
|
1252
|
+
frame_receipt_sha256: str,
|
|
1253
|
+
request_sha256: str,
|
|
1254
|
+
layout_sha256: str | None,
|
|
1255
|
+
block_request_sha256: str | None,
|
|
1256
|
+
block_spec_sha256: str | None,
|
|
1257
|
+
block_index: int | None,
|
|
1258
|
+
global_row_start: int,
|
|
1259
|
+
global_row_stop: int,
|
|
1260
|
+
subset_binding: ActionForecastBlockHealthSubsetBinding | None,
|
|
1261
|
+
health_policy: ActionForecastHealthPolicyBinding,
|
|
1262
|
+
normalized: _NormalizedActionForecastHealth,
|
|
1263
|
+
) -> ResolvedActionForecastHealthAssessment:
|
|
1264
|
+
return ResolvedActionForecastHealthAssessment(
|
|
1265
|
+
member_id=member_id,
|
|
1266
|
+
frame_kind=frame_kind,
|
|
1267
|
+
frame_receipt_sha256=frame_receipt_sha256,
|
|
1268
|
+
request_sha256=request_sha256,
|
|
1269
|
+
layout_sha256=layout_sha256,
|
|
1270
|
+
block_request_sha256=block_request_sha256,
|
|
1271
|
+
block_spec_sha256=block_spec_sha256,
|
|
1272
|
+
block_index=block_index,
|
|
1273
|
+
global_row_start=global_row_start,
|
|
1274
|
+
global_row_stop=global_row_stop,
|
|
1275
|
+
subset_binding=subset_binding,
|
|
1276
|
+
health_policy=health_policy,
|
|
1277
|
+
metric_assessments=normalized.metric_assessments,
|
|
1278
|
+
distinct_row_signature_count=normalized.distinct_row_signature_count,
|
|
1279
|
+
distinct_probability_valid_count=(
|
|
1280
|
+
normalized.distinct_probability_valid_count
|
|
1281
|
+
),
|
|
1282
|
+
constant_confidence_metric_ids=(
|
|
1283
|
+
normalized.constant_confidence_metric_ids
|
|
1284
|
+
),
|
|
1285
|
+
threshold_applied=normalized.threshold_applied,
|
|
1286
|
+
passes=normalized.passes,
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
def assess_resolved_action_forecast_health(
|
|
1291
|
+
request: ActionForecastRequest,
|
|
1292
|
+
batch: ResolvedActionForecastBatch,
|
|
1293
|
+
*,
|
|
1294
|
+
member_id: str,
|
|
1295
|
+
health_policy: ActionForecastHealthPolicyBinding,
|
|
1296
|
+
) -> ResolvedActionForecastHealthAssessment:
|
|
1297
|
+
"""Assess one validated complete batch in metric-scale units."""
|
|
1298
|
+
|
|
1299
|
+
if type(request) is not ActionForecastRequest:
|
|
1300
|
+
raise TypeError("request must be an exact ActionForecastRequest")
|
|
1301
|
+
request.__post_init__()
|
|
1302
|
+
validate_resolved_action_forecasts(request, batch)
|
|
1303
|
+
_validate_health_inputs(member_id=member_id, health_policy=health_policy)
|
|
1304
|
+
normalized = _assess_normalized_forecasts(
|
|
1305
|
+
batch.forecasts,
|
|
1306
|
+
request,
|
|
1307
|
+
health_policy,
|
|
1308
|
+
)
|
|
1309
|
+
return _bind_health_assessment(
|
|
1310
|
+
member_id=member_id,
|
|
1311
|
+
frame_kind=ActionForecastHealthFrameKind.COMPLETE,
|
|
1312
|
+
frame_receipt_sha256=batch.receipt_sha256,
|
|
1313
|
+
request_sha256=request.request_sha256,
|
|
1314
|
+
layout_sha256=None,
|
|
1315
|
+
block_request_sha256=None,
|
|
1316
|
+
block_spec_sha256=None,
|
|
1317
|
+
block_index=None,
|
|
1318
|
+
global_row_start=0,
|
|
1319
|
+
global_row_stop=len(batch.forecasts),
|
|
1320
|
+
subset_binding=None,
|
|
1321
|
+
health_policy=health_policy,
|
|
1322
|
+
normalized=normalized,
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
def assess_resolved_action_forecast_block_health(
|
|
1327
|
+
block_request: ActionForecastBlockRequest,
|
|
1328
|
+
block: ResolvedActionForecastBlock,
|
|
1329
|
+
*,
|
|
1330
|
+
member_id: str,
|
|
1331
|
+
health_policy: ActionForecastHealthPolicyBinding,
|
|
1332
|
+
) -> ResolvedActionForecastHealthAssessment:
|
|
1333
|
+
"""Assess one validated partition block without presenting it as a batch."""
|
|
1334
|
+
|
|
1335
|
+
if type(block_request) is not ActionForecastBlockRequest:
|
|
1336
|
+
raise TypeError("block_request must be an exact ActionForecastBlockRequest")
|
|
1337
|
+
block_request.__post_init__()
|
|
1338
|
+
validate_resolved_action_forecast_block(block_request, block)
|
|
1339
|
+
_validate_health_inputs(member_id=member_id, health_policy=health_policy)
|
|
1340
|
+
normalized = _assess_normalized_forecasts(
|
|
1341
|
+
block.forecasts,
|
|
1342
|
+
block_request.request,
|
|
1343
|
+
health_policy,
|
|
1344
|
+
)
|
|
1345
|
+
spec = block_request.block
|
|
1346
|
+
return _bind_health_assessment(
|
|
1347
|
+
member_id=member_id,
|
|
1348
|
+
frame_kind=ActionForecastHealthFrameKind.PARTITION_BLOCK,
|
|
1349
|
+
frame_receipt_sha256=block.receipt_sha256,
|
|
1350
|
+
request_sha256=block_request.request.request_sha256,
|
|
1351
|
+
layout_sha256=block_request.layout.layout_sha256,
|
|
1352
|
+
block_request_sha256=block_request.block_request_sha256,
|
|
1353
|
+
block_spec_sha256=spec.block_spec_sha256,
|
|
1354
|
+
block_index=spec.block_index,
|
|
1355
|
+
global_row_start=spec.global_row_start,
|
|
1356
|
+
global_row_stop=spec.global_row_stop,
|
|
1357
|
+
subset_binding=None,
|
|
1358
|
+
health_policy=health_policy,
|
|
1359
|
+
normalized=normalized,
|
|
1360
|
+
)
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
def assess_resolved_action_forecast_block_subset_health(
|
|
1364
|
+
block_request: ActionForecastBlockRequest,
|
|
1365
|
+
block: ResolvedActionForecastBlock,
|
|
1366
|
+
*,
|
|
1367
|
+
member_id: str,
|
|
1368
|
+
health_policy: ActionForecastHealthPolicyBinding,
|
|
1369
|
+
subset_policy: ActionForecastHealthSubsetPolicyBinding,
|
|
1370
|
+
included_global_row_indices: tuple[int, ...],
|
|
1371
|
+
) -> ResolvedActionForecastHealthAssessment:
|
|
1372
|
+
"""Assess one authenticated ordered subset of a validated partition block."""
|
|
1373
|
+
|
|
1374
|
+
if type(block_request) is not ActionForecastBlockRequest:
|
|
1375
|
+
raise TypeError("block_request must be an exact ActionForecastBlockRequest")
|
|
1376
|
+
block_request.__post_init__()
|
|
1377
|
+
validate_resolved_action_forecast_block(block_request, block)
|
|
1378
|
+
_validate_health_inputs(member_id=member_id, health_policy=health_policy)
|
|
1379
|
+
if type(subset_policy) is not ActionForecastHealthSubsetPolicyBinding:
|
|
1380
|
+
raise TypeError("subset_policy must be an exact subset-policy binding")
|
|
1381
|
+
subset_policy.__post_init__()
|
|
1382
|
+
if type(included_global_row_indices) is not tuple or not (
|
|
1383
|
+
included_global_row_indices
|
|
1384
|
+
) or any(type(value) is not int for value in included_global_row_indices):
|
|
1385
|
+
raise ValueError(
|
|
1386
|
+
"included_global_row_indices must be a non-empty exact tuple"
|
|
1387
|
+
)
|
|
1388
|
+
if included_global_row_indices != tuple(
|
|
1389
|
+
sorted(set(included_global_row_indices))
|
|
1390
|
+
):
|
|
1391
|
+
raise ValueError("included global rows must be unique and in block order")
|
|
1392
|
+
spec = block_request.block
|
|
1393
|
+
if any(
|
|
1394
|
+
value < spec.global_row_start or value >= spec.global_row_stop
|
|
1395
|
+
for value in included_global_row_indices
|
|
1396
|
+
):
|
|
1397
|
+
raise ValueError("an included global row is outside the parent block")
|
|
1398
|
+
|
|
1399
|
+
included_option_identities = tuple(
|
|
1400
|
+
block_request.request.finite_variation_contract.options[
|
|
1401
|
+
global_index
|
|
1402
|
+
].identity_sha256
|
|
1403
|
+
for global_index in included_global_row_indices
|
|
1404
|
+
)
|
|
1405
|
+
subset_binding = ActionForecastBlockHealthSubsetBinding(
|
|
1406
|
+
subset_policy=subset_policy,
|
|
1407
|
+
request_sha256=block_request.request.request_sha256,
|
|
1408
|
+
layout_sha256=block_request.layout.layout_sha256,
|
|
1409
|
+
block_request_sha256=block_request.block_request_sha256,
|
|
1410
|
+
block_spec_sha256=spec.block_spec_sha256,
|
|
1411
|
+
parent_block_receipt_sha256=block.receipt_sha256,
|
|
1412
|
+
block_index=spec.block_index,
|
|
1413
|
+
global_row_start=spec.global_row_start,
|
|
1414
|
+
global_row_stop=spec.global_row_stop,
|
|
1415
|
+
included_global_row_indices=included_global_row_indices,
|
|
1416
|
+
included_option_identity_sha256s=included_option_identities,
|
|
1417
|
+
)
|
|
1418
|
+
selected_forecasts = tuple(
|
|
1419
|
+
block.forecasts[global_index - spec.global_row_start]
|
|
1420
|
+
for global_index in included_global_row_indices
|
|
1421
|
+
)
|
|
1422
|
+
normalized = _assess_normalized_forecasts(
|
|
1423
|
+
selected_forecasts,
|
|
1424
|
+
block_request.request,
|
|
1425
|
+
health_policy,
|
|
1426
|
+
)
|
|
1427
|
+
return _bind_health_assessment(
|
|
1428
|
+
member_id=member_id,
|
|
1429
|
+
frame_kind=ActionForecastHealthFrameKind.PARTITION_BLOCK_SUBSET,
|
|
1430
|
+
frame_receipt_sha256=subset_binding.binding_sha256,
|
|
1431
|
+
request_sha256=block_request.request.request_sha256,
|
|
1432
|
+
layout_sha256=block_request.layout.layout_sha256,
|
|
1433
|
+
block_request_sha256=block_request.block_request_sha256,
|
|
1434
|
+
block_spec_sha256=spec.block_spec_sha256,
|
|
1435
|
+
block_index=spec.block_index,
|
|
1436
|
+
global_row_start=spec.global_row_start,
|
|
1437
|
+
global_row_stop=spec.global_row_stop,
|
|
1438
|
+
subset_binding=subset_binding,
|
|
1439
|
+
health_policy=health_policy,
|
|
1440
|
+
normalized=normalized,
|
|
1441
|
+
)
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
__all__ = [
|
|
1445
|
+
"ActionForecastBlockFailure",
|
|
1446
|
+
"ActionForecastBlockHealthSubsetBinding",
|
|
1447
|
+
"ActionForecastHealthFrameKind",
|
|
1448
|
+
"ActionForecastHealthPolicyBinding",
|
|
1449
|
+
"ActionForecastHealthSubsetPolicyBinding",
|
|
1450
|
+
"ActionForecastMetricHealth",
|
|
1451
|
+
"ActionForecastWaveError",
|
|
1452
|
+
"ConcurrentActionForecastWave",
|
|
1453
|
+
"LENIENT_ACTION_FORECAST_HEALTH_POLICY_DEFINITION_SHA256",
|
|
1454
|
+
"LENIENT_ACTION_FORECAST_HEALTH_POLICY_ID",
|
|
1455
|
+
"LENIENT_ACTION_FORECAST_HEALTH_POLICY_VERSION",
|
|
1456
|
+
"LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_DEFINITION_SHA256",
|
|
1457
|
+
"LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_ID",
|
|
1458
|
+
"LENIENT_ACTION_FORECAST_HEALTH_V2_POLICY_VERSION",
|
|
1459
|
+
"PartitionedActionForecastPolicy",
|
|
1460
|
+
"PartitionedActionForecastResult",
|
|
1461
|
+
"ResolvedActionForecastHealthAssessment",
|
|
1462
|
+
"action_forecast_block_call_id",
|
|
1463
|
+
"assess_resolved_action_forecast_block_health",
|
|
1464
|
+
"assess_resolved_action_forecast_block_subset_health",
|
|
1465
|
+
"assess_resolved_action_forecast_health",
|
|
1466
|
+
"assemble_partitioned_action_forecasts",
|
|
1467
|
+
"build_action_forecast_block_requests",
|
|
1468
|
+
"build_action_forecast_partition_layout",
|
|
1469
|
+
"lenient_action_forecast_health_policy",
|
|
1470
|
+
"lenient_action_forecast_health_v2_policy",
|
|
1471
|
+
]
|