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,1561 @@
|
|
|
1
|
+
"""High-level agentic generation port used by the evolutionary workflow."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import math
|
|
7
|
+
import re
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any, Protocol, Tuple, runtime_checkable
|
|
12
|
+
|
|
13
|
+
from agent_evolve.domain.finite_variation import (
|
|
14
|
+
FiniteVariationContract,
|
|
15
|
+
FiniteVariationOption,
|
|
16
|
+
validate_finite_variation_contract,
|
|
17
|
+
)
|
|
18
|
+
from agent_evolve.domain.ids import LLMCallId
|
|
19
|
+
from agent_evolve.domain.patch import (
|
|
20
|
+
ArrayIndex,
|
|
21
|
+
JsonPath,
|
|
22
|
+
ObjectKey,
|
|
23
|
+
require_sha256,
|
|
24
|
+
validate_json_path,
|
|
25
|
+
)
|
|
26
|
+
from agent_evolve.domain.typed_json import (
|
|
27
|
+
FrozenJsonArray,
|
|
28
|
+
FrozenJsonObject,
|
|
29
|
+
FrozenJsonValue,
|
|
30
|
+
canonical_typed_json_bytes,
|
|
31
|
+
freeze_json,
|
|
32
|
+
is_json_scalar,
|
|
33
|
+
typed_json_equal,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_LOWER_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
38
|
+
_FINITE_OPTION_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,255}$")
|
|
39
|
+
_CROSSOVER_LOCUS_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
|
|
40
|
+
_REFLECTION_METRIC_ID = re.compile(r"^[a-z][a-z0-9_.:-]{0,191}$")
|
|
41
|
+
_FINITE_OPTION_FAMILY = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
42
|
+
_REFLECTION_SEMANTIC_TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,191}$")
|
|
43
|
+
_REFLECTION_DECISION_PATH = re.compile(r"^\$([.\[].*)?$")
|
|
44
|
+
_REFLECTION_INSIGHT_CONTRACT_DOMAIN = b"agent-evolve:reflection-insight-contract:v2\x00"
|
|
45
|
+
_REFLECTION_INSIGHT_CONTRACT_V3_DOMAIN = (
|
|
46
|
+
b"agent-evolve:reflection-insight-contract:v3\x00"
|
|
47
|
+
)
|
|
48
|
+
_REFLECTION_EVIDENCE_CATALOG_DOMAIN = b"agent-evolve:reflection-evidence-catalog:v1\x00"
|
|
49
|
+
_INSIGHT_DRAFT_CONTENT_DOMAIN = b"agent-evolve:insight-draft-content:v1\x00"
|
|
50
|
+
_INSIGHT_DRAFT_HYPOTHESIS_DOMAIN = (
|
|
51
|
+
b"agent-evolve:insight-draft-unverified-hypothesis:v1\x00"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Shared by the prompt policy and the provider-visible structured-output schema.
|
|
55
|
+
# Keeping this at the port boundary prevents adapters from inventing different
|
|
56
|
+
# meanings for the same candidate-relative evidence paths.
|
|
57
|
+
CANDIDATE_COMPONENT_PATH_CONTRACT = (
|
|
58
|
+
"For every intended_changes entry and source_attribution.path, '$' denotes "
|
|
59
|
+
"the returned configuration value itself, not the enclosing proposal and "
|
|
60
|
+
"not a parent-evidence record. For example, when configuration is "
|
|
61
|
+
'{"gene":1,"settings":{"rate":2}}, use "$.gene" and '
|
|
62
|
+
'"$.settings.rate". For that example, "$.configuration.gene", '
|
|
63
|
+
'"$.parents[0].configuration.gene", and "$" are invalid component paths. '
|
|
64
|
+
"Never copy an enclosing configuration or parents wrapper into a path; "
|
|
65
|
+
'"$.configuration..." is valid only when "configuration" is genuinely a '
|
|
66
|
+
"top-level component inside the candidate configuration."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Shared by the generic two-parent prompt and its provider-visible output
|
|
70
|
+
# schema. Left/right claims are executable provenance: the engine verifies the
|
|
71
|
+
# witness and copies the exact immutable subtree from the named parent.
|
|
72
|
+
TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT = (
|
|
73
|
+
"For a two-parent crossover, source='left' or source='right' is valid at a "
|
|
74
|
+
"path only when the intended child matches that named parent and differs "
|
|
75
|
+
"from the other parent at the same path. The engine treats each left/right "
|
|
76
|
+
"claim as an executable inheritance instruction and copies the exact "
|
|
77
|
+
"immutable parent subtree; the returned configuration is a consistency "
|
|
78
|
+
"witness, not authority for inherited values. Its structure and nonnumeric "
|
|
79
|
+
"values must match exactly; each finite float leaf may differ by at most "
|
|
80
|
+
"one binary64 ULP to accommodate decimal serialization drift. A path where "
|
|
81
|
+
"both parents have "
|
|
82
|
+
"identical values proves neither side and must not be attributed to left "
|
|
83
|
+
"or right. If the child preserves that shared value, omit the path from "
|
|
84
|
+
"both source_attribution and intended_changes; if the child changes it, "
|
|
85
|
+
"attribute the genuinely new value as synthesized. Synthesized values "
|
|
86
|
+
"remain model-authored and must differ from both parents at their explicit "
|
|
87
|
+
"paths. A structural deletion must be attributed as synthesized at the "
|
|
88
|
+
"smallest retained containing object or array whose complete child value "
|
|
89
|
+
"reflects the deletion; an omitted path cannot itself be claimed. Claims "
|
|
90
|
+
"must be unique, non-overlapping, and collectively account for every "
|
|
91
|
+
"parent/child difference. Any unclaimed value shared by both parents must "
|
|
92
|
+
"remain present and exactly equal in the child. Include at least one "
|
|
93
|
+
"discriminating left contribution and at least one discriminating right "
|
|
94
|
+
"contribution."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
MAX_REFLECTION_EVIDENCE_CATALOG_ENTRIES = 256
|
|
98
|
+
MAX_REFLECTION_SEMANTIC_VOCABULARY_ENTRIES = 256
|
|
99
|
+
MAX_REFLECTION_DECISION_PATH_CHARS = 4_096
|
|
100
|
+
_REFLECTION_EVIDENCE_CITATION_KEY = re.compile(r"^e[0-9]{4}$")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _validate_contrast_ids(values: tuple[str, ...], *, name: str) -> None:
|
|
104
|
+
if type(values) is not tuple or any(
|
|
105
|
+
type(value) is not str or _LOWER_SHA256.fullmatch(value) is None
|
|
106
|
+
for value in values
|
|
107
|
+
):
|
|
108
|
+
raise TypeError(f"{name} must be an exact tuple of lowercase SHA-256 IDs")
|
|
109
|
+
if values != tuple(sorted(set(values))):
|
|
110
|
+
raise ValueError(f"{name} must be unique and canonically sorted")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _validate_canonical_tokens(
|
|
114
|
+
values: tuple[str, ...],
|
|
115
|
+
*,
|
|
116
|
+
name: str,
|
|
117
|
+
pattern: re.Pattern[str],
|
|
118
|
+
) -> None:
|
|
119
|
+
if type(values) is not tuple or any(
|
|
120
|
+
type(value) is not str or pattern.fullmatch(value) is None for value in values
|
|
121
|
+
):
|
|
122
|
+
raise TypeError(f"{name} must be an exact tuple of canonical identifiers")
|
|
123
|
+
if not values:
|
|
124
|
+
raise ValueError(f"{name} must be non-empty")
|
|
125
|
+
if values != tuple(sorted(set(values))):
|
|
126
|
+
raise ValueError(f"{name} must be unique and canonically sorted")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _validate_canonical_enum_values(
|
|
130
|
+
values: tuple[Enum, ...],
|
|
131
|
+
*,
|
|
132
|
+
name: str,
|
|
133
|
+
enum_type: type[Enum],
|
|
134
|
+
allow_empty: bool = False,
|
|
135
|
+
) -> None:
|
|
136
|
+
if type(values) is not tuple or any(
|
|
137
|
+
type(value) is not enum_type for value in values
|
|
138
|
+
):
|
|
139
|
+
raise TypeError(f"{name} must be an exact tuple of {enum_type.__name__} values")
|
|
140
|
+
if not values and not allow_empty:
|
|
141
|
+
raise ValueError(f"{name} must be non-empty")
|
|
142
|
+
canonical = tuple(sorted(set(values), key=lambda value: str(value.value)))
|
|
143
|
+
if values != canonical:
|
|
144
|
+
raise ValueError(f"{name} must be unique and canonically sorted")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _validate_decision_paths(values: tuple[str, ...], *, name: str) -> None:
|
|
148
|
+
if type(values) is not tuple or any(type(value) is not str for value in values):
|
|
149
|
+
raise TypeError(f"{name} must be an exact tuple of strings")
|
|
150
|
+
if not values:
|
|
151
|
+
raise ValueError(f"{name} must be non-empty")
|
|
152
|
+
if any(
|
|
153
|
+
len(value) > MAX_REFLECTION_DECISION_PATH_CHARS
|
|
154
|
+
or _REFLECTION_DECISION_PATH.fullmatch(value) is None
|
|
155
|
+
for value in values
|
|
156
|
+
):
|
|
157
|
+
raise ValueError(f"{name} must contain bounded rooted decision paths")
|
|
158
|
+
if len(values) > MAX_REFLECTION_SEMANTIC_VOCABULARY_ENTRIES:
|
|
159
|
+
raise ValueError(f"{name} exceeds MAX_REFLECTION_SEMANTIC_VOCABULARY_ENTRIES")
|
|
160
|
+
if values != tuple(sorted(set(values))):
|
|
161
|
+
raise ValueError(f"{name} must be unique and canonically sorted")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@dataclass(frozen=True, slots=True)
|
|
165
|
+
class ReflectionEvidenceCatalogEntry:
|
|
166
|
+
"""One request-local short citation bound to a canonical contrast ID."""
|
|
167
|
+
|
|
168
|
+
citation_key: str
|
|
169
|
+
contrast_id: str
|
|
170
|
+
|
|
171
|
+
def __post_init__(self) -> None:
|
|
172
|
+
if (
|
|
173
|
+
type(self.citation_key) is not str
|
|
174
|
+
or _REFLECTION_EVIDENCE_CITATION_KEY.fullmatch(self.citation_key) is None
|
|
175
|
+
):
|
|
176
|
+
raise ValueError("citation_key must use the eNNNN grammar")
|
|
177
|
+
if (
|
|
178
|
+
type(self.contrast_id) is not str
|
|
179
|
+
or _LOWER_SHA256.fullmatch(self.contrast_id) is None
|
|
180
|
+
):
|
|
181
|
+
raise ValueError("contrast_id must be a lowercase SHA-256 ID")
|
|
182
|
+
|
|
183
|
+
def to_record(self) -> dict[str, str]:
|
|
184
|
+
ReflectionEvidenceCatalogEntry.__post_init__(self)
|
|
185
|
+
return {
|
|
186
|
+
"citation_key": self.citation_key,
|
|
187
|
+
"contrast_id": self.contrast_id,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@dataclass(frozen=True, slots=True)
|
|
192
|
+
class ReflectionEvidenceCatalog:
|
|
193
|
+
"""Authenticated deterministic short keys for one reflection request.
|
|
194
|
+
|
|
195
|
+
The catalog does not alter the evidence universe. It is derived from the
|
|
196
|
+
request's already-canonical contrast IDs and merely replaces error-prone
|
|
197
|
+
model reproduction of 64-character hashes with closed ``eNNNN`` literals.
|
|
198
|
+
Resolution is exact: prefixes, fuzzy matches, and foreign keys are never
|
|
199
|
+
accepted.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
entries: Tuple[ReflectionEvidenceCatalogEntry, ...]
|
|
203
|
+
catalog_identity_sha256: str = field(init=False)
|
|
204
|
+
|
|
205
|
+
def __post_init__(self) -> None:
|
|
206
|
+
if type(self.entries) is not tuple or not self.entries:
|
|
207
|
+
raise ValueError("entries must be a non-empty exact tuple")
|
|
208
|
+
if len(self.entries) > MAX_REFLECTION_EVIDENCE_CATALOG_ENTRIES:
|
|
209
|
+
raise ValueError("entries exceed MAX_REFLECTION_EVIDENCE_CATALOG_ENTRIES")
|
|
210
|
+
if any(
|
|
211
|
+
type(entry) is not ReflectionEvidenceCatalogEntry for entry in self.entries
|
|
212
|
+
):
|
|
213
|
+
raise TypeError(
|
|
214
|
+
"entries must contain exact ReflectionEvidenceCatalogEntry values"
|
|
215
|
+
)
|
|
216
|
+
for entry in self.entries:
|
|
217
|
+
ReflectionEvidenceCatalogEntry.__post_init__(entry)
|
|
218
|
+
contrast_ids = tuple(entry.contrast_id for entry in self.entries)
|
|
219
|
+
_validate_contrast_ids(contrast_ids, name="catalog contrast_ids")
|
|
220
|
+
expected_keys = tuple(
|
|
221
|
+
f"e{index:04d}" for index in range(1, len(self.entries) + 1)
|
|
222
|
+
)
|
|
223
|
+
if self.citation_keys != expected_keys:
|
|
224
|
+
raise ValueError(
|
|
225
|
+
"citation keys must be contiguous e0001..eNNNN in contrast-ID order"
|
|
226
|
+
)
|
|
227
|
+
object.__setattr__(
|
|
228
|
+
self,
|
|
229
|
+
"catalog_identity_sha256",
|
|
230
|
+
hashlib.sha256(
|
|
231
|
+
_REFLECTION_EVIDENCE_CATALOG_DOMAIN
|
|
232
|
+
+ canonical_typed_json_bytes(freeze_json(self._unsigned_record()))
|
|
233
|
+
).hexdigest(),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
@classmethod
|
|
237
|
+
def from_contrast_ids(
|
|
238
|
+
cls,
|
|
239
|
+
contrast_ids: Tuple[str, ...],
|
|
240
|
+
) -> "ReflectionEvidenceCatalog":
|
|
241
|
+
"""Derive the only canonical key assignment for an evidence universe."""
|
|
242
|
+
|
|
243
|
+
_validate_contrast_ids(contrast_ids, name="contrast_ids")
|
|
244
|
+
if not contrast_ids:
|
|
245
|
+
raise ValueError("a reflection evidence catalog cannot be empty")
|
|
246
|
+
if len(contrast_ids) > MAX_REFLECTION_EVIDENCE_CATALOG_ENTRIES:
|
|
247
|
+
raise ValueError(
|
|
248
|
+
"contrast_ids exceed MAX_REFLECTION_EVIDENCE_CATALOG_ENTRIES"
|
|
249
|
+
)
|
|
250
|
+
return cls(
|
|
251
|
+
tuple(
|
|
252
|
+
ReflectionEvidenceCatalogEntry(
|
|
253
|
+
citation_key=f"e{index:04d}",
|
|
254
|
+
contrast_id=contrast_id,
|
|
255
|
+
)
|
|
256
|
+
for index, contrast_id in enumerate(contrast_ids, start=1)
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def citation_keys(self) -> Tuple[str, ...]:
|
|
262
|
+
return tuple(entry.citation_key for entry in self.entries)
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def contrast_ids(self) -> Tuple[str, ...]:
|
|
266
|
+
return tuple(entry.contrast_id for entry in self.entries)
|
|
267
|
+
|
|
268
|
+
def citation_key_for_contrast_id(self, contrast_id: str) -> str:
|
|
269
|
+
"""Return the exact request-local citation key for one full ID.
|
|
270
|
+
|
|
271
|
+
Keeping this lookup on the authenticated catalog prevents composition
|
|
272
|
+
roots from duplicating its field names or rebuilding a parallel map.
|
|
273
|
+
Unknown, malformed, and non-string IDs fail closed.
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
ReflectionEvidenceCatalog.__post_init__(self)
|
|
277
|
+
if type(contrast_id) is not str or _LOWER_SHA256.fullmatch(contrast_id) is None:
|
|
278
|
+
raise ValueError("contrast_id must be a lowercase SHA-256 ID")
|
|
279
|
+
for entry in self.entries:
|
|
280
|
+
if entry.contrast_id == contrast_id:
|
|
281
|
+
return entry.citation_key
|
|
282
|
+
raise ValueError("contrast_id is not present in this evidence catalog")
|
|
283
|
+
|
|
284
|
+
def resolve_citation_keys(
|
|
285
|
+
self,
|
|
286
|
+
citation_keys: Tuple[str, ...],
|
|
287
|
+
) -> Tuple[str, ...]:
|
|
288
|
+
"""Resolve exact keys to canonical full IDs without corrective matching."""
|
|
289
|
+
|
|
290
|
+
ReflectionEvidenceCatalog.__post_init__(self)
|
|
291
|
+
if type(citation_keys) is not tuple or any(
|
|
292
|
+
type(value) is not str for value in citation_keys
|
|
293
|
+
):
|
|
294
|
+
raise TypeError("citation_keys must be an exact tuple of strings")
|
|
295
|
+
if not citation_keys:
|
|
296
|
+
raise ValueError("citation_keys cannot be empty")
|
|
297
|
+
if len(set(citation_keys)) != len(citation_keys):
|
|
298
|
+
raise ValueError("citation_keys cannot contain duplicates")
|
|
299
|
+
lookup = {entry.citation_key: entry.contrast_id for entry in self.entries}
|
|
300
|
+
if not set(citation_keys).issubset(lookup):
|
|
301
|
+
raise ValueError("citation_keys contain an unknown or foreign key")
|
|
302
|
+
return tuple(sorted(lookup[key] for key in citation_keys))
|
|
303
|
+
|
|
304
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
305
|
+
return {
|
|
306
|
+
"schema_version": 1,
|
|
307
|
+
"entries": [entry.to_record() for entry in self.entries],
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
def to_record(self) -> dict[str, object]:
|
|
311
|
+
ReflectionEvidenceCatalog.__post_init__(self)
|
|
312
|
+
return {
|
|
313
|
+
**self._unsigned_record(),
|
|
314
|
+
"catalog_identity_sha256": self.catalog_identity_sha256,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class MetricEffectDirection(str, Enum):
|
|
319
|
+
"""Closed, testable prediction for one explicitly named numeric metric."""
|
|
320
|
+
|
|
321
|
+
DECREASE = "decrease"
|
|
322
|
+
INCREASE = "increase"
|
|
323
|
+
UNCHANGED = "unchanged"
|
|
324
|
+
UNKNOWN = "unknown"
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class ReflectionInsightKind(str, Enum):
|
|
328
|
+
"""Closed epistemic category for a model-authored reflection insight.
|
|
329
|
+
|
|
330
|
+
Kind deliberately does not encode where an insight may be consumed. That
|
|
331
|
+
separate scope prevents a mechanistic conjecture, for example, from
|
|
332
|
+
silently becoming authorization for every evolutionary operator.
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
EMPIRICAL_PREDICTIVE_RULE = "empirical_predictive_rule"
|
|
336
|
+
MECHANISTIC_CONJECTURE = "mechanistic_conjecture"
|
|
337
|
+
SEARCH_HEURISTIC = "search_heuristic"
|
|
338
|
+
CONTRACT_INVARIANT = "contract_invariant"
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class ReflectionConsumerScope(str, Enum):
|
|
342
|
+
"""Closed evolutionary decisions that may consume an insight."""
|
|
343
|
+
|
|
344
|
+
MUTATION_SELECTION = "mutation_selection"
|
|
345
|
+
RECOMBINATION_SELECTION = "recombination_selection"
|
|
346
|
+
PARENT_SELECTION = "parent_selection"
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class MetricComparisonAnchorKind(str, Enum):
|
|
350
|
+
"""Closed reference point for interpreting one metric direction."""
|
|
351
|
+
|
|
352
|
+
CURRENT_PARENT = "current_parent"
|
|
353
|
+
NAMED_SOURCE_ROLE = "named_source_role"
|
|
354
|
+
COMMON_ANCESTOR = "common_ancestor"
|
|
355
|
+
FROZEN_ARCHIVE_INCUMBENT = "frozen_archive_incumbent"
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
@dataclass(frozen=True, slots=True)
|
|
359
|
+
class MetricComparisonAnchor:
|
|
360
|
+
"""Typed comparison reference, optionally bound to an adapter-owned role."""
|
|
361
|
+
|
|
362
|
+
kind: MetricComparisonAnchorKind
|
|
363
|
+
source_role_id: str | None = None
|
|
364
|
+
|
|
365
|
+
def __post_init__(self) -> None:
|
|
366
|
+
if type(self.kind) is not MetricComparisonAnchorKind:
|
|
367
|
+
raise TypeError("kind must be an exact MetricComparisonAnchorKind")
|
|
368
|
+
if self.kind is MetricComparisonAnchorKind.NAMED_SOURCE_ROLE:
|
|
369
|
+
if (
|
|
370
|
+
type(self.source_role_id) is not str
|
|
371
|
+
or _REFLECTION_SEMANTIC_TOKEN.fullmatch(self.source_role_id) is None
|
|
372
|
+
):
|
|
373
|
+
raise ValueError(
|
|
374
|
+
"named_source_role anchors require a canonical source_role_id"
|
|
375
|
+
)
|
|
376
|
+
elif self.source_role_id is not None:
|
|
377
|
+
raise ValueError(
|
|
378
|
+
"source_role_id is valid only for named_source_role anchors"
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def to_record(self) -> dict[str, str | None]:
|
|
382
|
+
MetricComparisonAnchor.__post_init__(self)
|
|
383
|
+
return {
|
|
384
|
+
"kind": self.kind.value,
|
|
385
|
+
"source_role_id": self.source_role_id,
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@dataclass(frozen=True, slots=True)
|
|
390
|
+
class MetricEffectPrediction:
|
|
391
|
+
"""One direction prediction, independent of any domain's goal semantics."""
|
|
392
|
+
|
|
393
|
+
metric_id: str
|
|
394
|
+
direction: MetricEffectDirection
|
|
395
|
+
comparison_anchor: MetricComparisonAnchor | None = None
|
|
396
|
+
|
|
397
|
+
def __post_init__(self) -> None:
|
|
398
|
+
if (
|
|
399
|
+
type(self.metric_id) is not str
|
|
400
|
+
or _REFLECTION_METRIC_ID.fullmatch(self.metric_id) is None
|
|
401
|
+
):
|
|
402
|
+
raise ValueError(
|
|
403
|
+
"metric_id must use the closed reflection identifier grammar"
|
|
404
|
+
)
|
|
405
|
+
if type(self.direction) is not MetricEffectDirection:
|
|
406
|
+
raise TypeError("direction must be an exact MetricEffectDirection")
|
|
407
|
+
if self.comparison_anchor is not None:
|
|
408
|
+
if type(self.comparison_anchor) is not MetricComparisonAnchor:
|
|
409
|
+
raise TypeError(
|
|
410
|
+
"comparison_anchor must be an exact MetricComparisonAnchor or None"
|
|
411
|
+
)
|
|
412
|
+
MetricComparisonAnchor.__post_init__(self.comparison_anchor)
|
|
413
|
+
|
|
414
|
+
def to_record(self) -> dict[str, object]:
|
|
415
|
+
MetricEffectPrediction.__post_init__(self)
|
|
416
|
+
record: dict[str, object] = {
|
|
417
|
+
"metric_id": self.metric_id,
|
|
418
|
+
"direction": self.direction.value,
|
|
419
|
+
}
|
|
420
|
+
if self.comparison_anchor is not None:
|
|
421
|
+
record["comparison_anchor"] = self.comparison_anchor.to_record()
|
|
422
|
+
return record
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@dataclass(frozen=True, slots=True)
|
|
426
|
+
class SourceAttribution:
|
|
427
|
+
path: str
|
|
428
|
+
source: str
|
|
429
|
+
|
|
430
|
+
def __post_init__(self) -> None:
|
|
431
|
+
if type(self.path) is not str or not self.path.strip():
|
|
432
|
+
raise ValueError("attribution path must be non-empty")
|
|
433
|
+
if self.source not in {"ancestor", "left", "right", "synthesized", "mutation"}:
|
|
434
|
+
raise ValueError("unsupported attribution source")
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@dataclass(frozen=True, slots=True)
|
|
438
|
+
class ConflictResolutionDraft:
|
|
439
|
+
relation_id: str
|
|
440
|
+
choice: str
|
|
441
|
+
explanation: str
|
|
442
|
+
|
|
443
|
+
def __post_init__(self) -> None:
|
|
444
|
+
if type(self.relation_id) is not str or not self.relation_id.strip():
|
|
445
|
+
raise ValueError("relation_id must be non-empty")
|
|
446
|
+
if self.choice not in {
|
|
447
|
+
"choose_left",
|
|
448
|
+
"choose_right",
|
|
449
|
+
"synthesize",
|
|
450
|
+
"drop_both",
|
|
451
|
+
}:
|
|
452
|
+
raise ValueError("unsupported conflict resolution choice")
|
|
453
|
+
if type(self.explanation) is not str or not self.explanation.strip():
|
|
454
|
+
raise ValueError("resolution explanation must be non-empty")
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
@dataclass(frozen=True, slots=True)
|
|
458
|
+
class CandidateDraft:
|
|
459
|
+
configuration: dict[str, Any]
|
|
460
|
+
design_rationale: str
|
|
461
|
+
intended_changes: Tuple[str, ...] = ()
|
|
462
|
+
source_attribution: Tuple[SourceAttribution, ...] = ()
|
|
463
|
+
claimed_insight_ids: Tuple[str, ...] = ()
|
|
464
|
+
claimed_preservation_obligation_ids: Tuple[str, ...] = ()
|
|
465
|
+
conflict_resolutions: Tuple[ConflictResolutionDraft, ...] = ()
|
|
466
|
+
|
|
467
|
+
def __post_init__(self) -> None:
|
|
468
|
+
if type(self.configuration) is not dict:
|
|
469
|
+
raise TypeError("configuration must be an exact dict")
|
|
470
|
+
if type(self.design_rationale) is not str or not self.design_rationale.strip():
|
|
471
|
+
raise ValueError("design_rationale must be non-empty")
|
|
472
|
+
for name in (
|
|
473
|
+
"intended_changes",
|
|
474
|
+
"claimed_insight_ids",
|
|
475
|
+
"claimed_preservation_obligation_ids",
|
|
476
|
+
):
|
|
477
|
+
values = getattr(self, name)
|
|
478
|
+
if type(values) is not tuple or any(
|
|
479
|
+
type(value) is not str or not value.strip() for value in values
|
|
480
|
+
):
|
|
481
|
+
raise TypeError(f"{name} must be an exact tuple of non-empty strings")
|
|
482
|
+
if type(self.source_attribution) is not tuple or any(
|
|
483
|
+
type(value) is not SourceAttribution for value in self.source_attribution
|
|
484
|
+
):
|
|
485
|
+
raise TypeError("source_attribution must contain exact values")
|
|
486
|
+
if type(self.conflict_resolutions) is not tuple or any(
|
|
487
|
+
type(value) is not ConflictResolutionDraft
|
|
488
|
+
for value in self.conflict_resolutions
|
|
489
|
+
):
|
|
490
|
+
raise TypeError("conflict_resolutions must contain exact values")
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def _frozen_value_at_path(
|
|
494
|
+
value: FrozenJsonValue,
|
|
495
|
+
path: JsonPath,
|
|
496
|
+
) -> FrozenJsonValue:
|
|
497
|
+
"""Resolve an already-frozen value without introducing a policy dependency."""
|
|
498
|
+
|
|
499
|
+
current = value
|
|
500
|
+
for segment in path.segments:
|
|
501
|
+
if type(segment) is ObjectKey:
|
|
502
|
+
if type(current) is not FrozenJsonObject:
|
|
503
|
+
raise ValueError("atomic mutation path reaches a non-object")
|
|
504
|
+
matches = tuple(item for key, item in current.items if key == segment.value)
|
|
505
|
+
if len(matches) != 1:
|
|
506
|
+
raise ValueError("atomic mutation path does not exist")
|
|
507
|
+
current = matches[0]
|
|
508
|
+
elif type(segment) is ArrayIndex:
|
|
509
|
+
if type(current) is not FrozenJsonArray:
|
|
510
|
+
raise ValueError("atomic mutation path reaches a non-array")
|
|
511
|
+
if segment.value >= len(current.items):
|
|
512
|
+
raise ValueError("atomic mutation path is out of bounds")
|
|
513
|
+
current = current.items[segment.value]
|
|
514
|
+
else: # pragma: no cover - JsonPath closes the segment union.
|
|
515
|
+
raise AssertionError("unsupported path segment")
|
|
516
|
+
return current
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
@dataclass(frozen=True, slots=True)
|
|
520
|
+
class AtomicMutationOutputContract:
|
|
521
|
+
"""Immutable parent and exact scalar leaf exposed to one patch-native call."""
|
|
522
|
+
|
|
523
|
+
parent_configuration: FrozenJsonObject
|
|
524
|
+
editable_path: JsonPath
|
|
525
|
+
replacement_options: Tuple[FrozenJsonValue, ...] = ()
|
|
526
|
+
|
|
527
|
+
def __post_init__(self) -> None:
|
|
528
|
+
if type(self.parent_configuration) is not FrozenJsonObject:
|
|
529
|
+
raise TypeError("parent_configuration must be an exact FrozenJsonObject")
|
|
530
|
+
if freeze_json(self.parent_configuration) is not self.parent_configuration:
|
|
531
|
+
raise TypeError("parent_configuration must already be frozen typed JSON")
|
|
532
|
+
if type(self.editable_path) is not JsonPath:
|
|
533
|
+
raise TypeError("editable_path must be an exact JsonPath")
|
|
534
|
+
validate_json_path(self.editable_path)
|
|
535
|
+
if not self.editable_path.segments:
|
|
536
|
+
raise ValueError("atomic mutation cannot replace the candidate root")
|
|
537
|
+
current = _frozen_value_at_path(
|
|
538
|
+
self.parent_configuration,
|
|
539
|
+
self.editable_path,
|
|
540
|
+
)
|
|
541
|
+
if not is_json_scalar(current):
|
|
542
|
+
raise ValueError("atomic mutation editable_path must resolve to a scalar")
|
|
543
|
+
if type(self.replacement_options) is not tuple:
|
|
544
|
+
raise TypeError("replacement_options must be an exact tuple")
|
|
545
|
+
canonical_options: list[bytes] = []
|
|
546
|
+
for option in self.replacement_options:
|
|
547
|
+
if freeze_json(option) is not option or not is_json_scalar(option):
|
|
548
|
+
raise TypeError(
|
|
549
|
+
"replacement_options must contain frozen typed-JSON scalars"
|
|
550
|
+
)
|
|
551
|
+
if typed_json_equal(current, option):
|
|
552
|
+
raise ValueError("replacement_options must exclude the parent value")
|
|
553
|
+
canonical_options.append(canonical_typed_json_bytes(option))
|
|
554
|
+
if len(set(canonical_options)) != len(canonical_options):
|
|
555
|
+
raise ValueError("replacement_options cannot contain duplicates")
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
@dataclass(frozen=True, slots=True)
|
|
559
|
+
class AtomicMutationDraft:
|
|
560
|
+
"""One model-authored scalar replacement; the engine owns materialization."""
|
|
561
|
+
|
|
562
|
+
path: JsonPath
|
|
563
|
+
replacement: FrozenJsonValue
|
|
564
|
+
design_rationale: str
|
|
565
|
+
claimed_insight_ids: Tuple[str, ...] = ()
|
|
566
|
+
|
|
567
|
+
def __post_init__(self) -> None:
|
|
568
|
+
if type(self.path) is not JsonPath:
|
|
569
|
+
raise TypeError("path must be an exact JsonPath")
|
|
570
|
+
validate_json_path(self.path)
|
|
571
|
+
if not self.path.segments:
|
|
572
|
+
raise ValueError("an atomic mutation path cannot be the root")
|
|
573
|
+
frozen_replacement = freeze_json(self.replacement)
|
|
574
|
+
if frozen_replacement is not self.replacement:
|
|
575
|
+
raise TypeError("replacement must already be frozen typed JSON")
|
|
576
|
+
if not is_json_scalar(self.replacement):
|
|
577
|
+
raise TypeError("replacement must be an exact typed-JSON scalar")
|
|
578
|
+
if type(self.design_rationale) is not str or not self.design_rationale.strip():
|
|
579
|
+
raise ValueError("design_rationale must be non-empty")
|
|
580
|
+
if type(self.claimed_insight_ids) is not tuple or any(
|
|
581
|
+
type(value) is not str or not value.strip()
|
|
582
|
+
for value in self.claimed_insight_ids
|
|
583
|
+
):
|
|
584
|
+
raise TypeError(
|
|
585
|
+
"claimed_insight_ids must be an exact tuple of non-empty strings"
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
@dataclass(frozen=True, slots=True)
|
|
590
|
+
class ExactParentCrossoverOutputContract:
|
|
591
|
+
"""Request-scoped finite vocabulary for an executable parent-import plan.
|
|
592
|
+
|
|
593
|
+
The engine owns parent configurations, locus paths, and materialization.
|
|
594
|
+
The provider sees only these opaque locus identifiers and selects a proper,
|
|
595
|
+
nonempty donor subset. Keeping parent payloads out of this port contract
|
|
596
|
+
prevents an integration adapter from becoming a second crossover engine.
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
contract_identity_sha256: str
|
|
600
|
+
locus_ids: Tuple[str, ...]
|
|
601
|
+
claimable_insight_ids: Tuple[str, ...] = ()
|
|
602
|
+
forbidden_import_locus_sets: Tuple[Tuple[str, ...], ...] = ()
|
|
603
|
+
|
|
604
|
+
def __post_init__(self) -> None:
|
|
605
|
+
require_sha256(
|
|
606
|
+
self.contract_identity_sha256,
|
|
607
|
+
"contract_identity_sha256",
|
|
608
|
+
)
|
|
609
|
+
if type(self.locus_ids) is not tuple or any(
|
|
610
|
+
type(value) is not str or _CROSSOVER_LOCUS_ID.fullmatch(value) is None
|
|
611
|
+
for value in self.locus_ids
|
|
612
|
+
):
|
|
613
|
+
raise TypeError("locus_ids must be an exact tuple of canonical identifiers")
|
|
614
|
+
if len(self.locus_ids) < 2:
|
|
615
|
+
raise ValueError("exact parent crossover requires at least two loci")
|
|
616
|
+
if self.locus_ids != tuple(sorted(set(self.locus_ids))):
|
|
617
|
+
raise ValueError("locus_ids must be unique and canonically sorted")
|
|
618
|
+
if type(self.claimable_insight_ids) is not tuple or any(
|
|
619
|
+
type(value) is not str or not value.strip()
|
|
620
|
+
for value in self.claimable_insight_ids
|
|
621
|
+
):
|
|
622
|
+
raise TypeError(
|
|
623
|
+
"claimable_insight_ids must be an exact tuple of non-empty strings"
|
|
624
|
+
)
|
|
625
|
+
if self.claimable_insight_ids != tuple(sorted(set(self.claimable_insight_ids))):
|
|
626
|
+
raise ValueError(
|
|
627
|
+
"claimable_insight_ids must be unique and canonically sorted"
|
|
628
|
+
)
|
|
629
|
+
if type(self.forbidden_import_locus_sets) is not tuple or any(
|
|
630
|
+
type(value) is not tuple for value in self.forbidden_import_locus_sets
|
|
631
|
+
):
|
|
632
|
+
raise TypeError(
|
|
633
|
+
"forbidden_import_locus_sets must be an exact tuple of tuples"
|
|
634
|
+
)
|
|
635
|
+
allowed = set(self.locus_ids)
|
|
636
|
+
for forbidden in self.forbidden_import_locus_sets:
|
|
637
|
+
if not forbidden or len(forbidden) >= len(self.locus_ids):
|
|
638
|
+
raise ValueError(
|
|
639
|
+
"each forbidden import locus set must be a proper nonempty subset"
|
|
640
|
+
)
|
|
641
|
+
if forbidden != tuple(sorted(set(forbidden))):
|
|
642
|
+
raise ValueError(
|
|
643
|
+
"forbidden import locus sets must use canonical unique IDs"
|
|
644
|
+
)
|
|
645
|
+
if not set(forbidden).issubset(allowed):
|
|
646
|
+
raise ValueError(
|
|
647
|
+
"forbidden import locus sets cannot escape the locus catalog"
|
|
648
|
+
)
|
|
649
|
+
if self.forbidden_import_locus_sets != tuple(
|
|
650
|
+
sorted(set(self.forbidden_import_locus_sets))
|
|
651
|
+
):
|
|
652
|
+
raise ValueError(
|
|
653
|
+
"forbidden import locus sets must be unique and canonically sorted"
|
|
654
|
+
)
|
|
655
|
+
if len(self.forbidden_import_locus_sets) == (1 << len(self.locus_ids)) - 2:
|
|
656
|
+
raise ValueError(
|
|
657
|
+
"forbidden import locus sets exhaust the exact crossover action space"
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
@dataclass(frozen=True, slots=True)
|
|
662
|
+
class ExactParentCrossoverDraft:
|
|
663
|
+
"""One bounded model choice; the engine owns the exact child and evidence."""
|
|
664
|
+
|
|
665
|
+
contract_identity_sha256: str
|
|
666
|
+
import_locus_ids: Tuple[str, ...]
|
|
667
|
+
claimed_insight_ids: Tuple[str, ...] = ()
|
|
668
|
+
|
|
669
|
+
def __post_init__(self) -> None:
|
|
670
|
+
require_sha256(
|
|
671
|
+
self.contract_identity_sha256,
|
|
672
|
+
"contract_identity_sha256",
|
|
673
|
+
)
|
|
674
|
+
if type(self.import_locus_ids) is not tuple or any(
|
|
675
|
+
type(value) is not str or _CROSSOVER_LOCUS_ID.fullmatch(value) is None
|
|
676
|
+
for value in self.import_locus_ids
|
|
677
|
+
):
|
|
678
|
+
raise TypeError(
|
|
679
|
+
"import_locus_ids must be an exact tuple of canonical identifiers"
|
|
680
|
+
)
|
|
681
|
+
if not self.import_locus_ids:
|
|
682
|
+
raise ValueError("a crossover plan must import at least one donor locus")
|
|
683
|
+
if self.import_locus_ids != tuple(sorted(set(self.import_locus_ids))):
|
|
684
|
+
raise ValueError("import_locus_ids must be unique and canonically sorted")
|
|
685
|
+
if type(self.claimed_insight_ids) is not tuple or any(
|
|
686
|
+
type(value) is not str or not value.strip()
|
|
687
|
+
for value in self.claimed_insight_ids
|
|
688
|
+
):
|
|
689
|
+
raise TypeError(
|
|
690
|
+
"claimed_insight_ids must be an exact tuple of non-empty strings"
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
@dataclass(frozen=True, slots=True)
|
|
695
|
+
class FiniteVariationSelectionDraft:
|
|
696
|
+
"""A model-selected ID bound to the exact presealed option and palette."""
|
|
697
|
+
|
|
698
|
+
option_id: str
|
|
699
|
+
option_identity_sha256: str
|
|
700
|
+
contract_identity_sha256: str
|
|
701
|
+
design_rationale: str
|
|
702
|
+
claimed_insight_ids: Tuple[str, ...] = ()
|
|
703
|
+
|
|
704
|
+
def __post_init__(self) -> None:
|
|
705
|
+
if (
|
|
706
|
+
type(self.option_id) is not str
|
|
707
|
+
or _FINITE_OPTION_ID.fullmatch(self.option_id) is None
|
|
708
|
+
):
|
|
709
|
+
raise ValueError(
|
|
710
|
+
"option_id must use the closed lowercase identifier grammar"
|
|
711
|
+
)
|
|
712
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
713
|
+
require_sha256(self.contract_identity_sha256, "contract_identity_sha256")
|
|
714
|
+
if type(self.design_rationale) is not str or not self.design_rationale.strip():
|
|
715
|
+
raise ValueError("design_rationale must be non-empty")
|
|
716
|
+
if type(self.claimed_insight_ids) is not tuple or any(
|
|
717
|
+
type(value) is not str or not value.strip()
|
|
718
|
+
for value in self.claimed_insight_ids
|
|
719
|
+
):
|
|
720
|
+
raise TypeError(
|
|
721
|
+
"claimed_insight_ids must be an exact tuple of non-empty strings"
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def resolve_finite_variation_selection(
|
|
726
|
+
contract: FiniteVariationContract,
|
|
727
|
+
draft: FiniteVariationSelectionDraft,
|
|
728
|
+
) -> FiniteVariationOption:
|
|
729
|
+
"""Verify selection receipts and return the one sealed full child."""
|
|
730
|
+
|
|
731
|
+
validate_finite_variation_contract(contract)
|
|
732
|
+
if type(draft) is not FiniteVariationSelectionDraft:
|
|
733
|
+
raise TypeError("draft must be an exact FiniteVariationSelectionDraft")
|
|
734
|
+
FiniteVariationSelectionDraft.__post_init__(draft)
|
|
735
|
+
if draft.contract_identity_sha256 != contract.identity_sha256:
|
|
736
|
+
raise ValueError("selection draft is bound to a different finite contract")
|
|
737
|
+
option = contract.resolve(draft.option_id)
|
|
738
|
+
if draft.option_identity_sha256 != option.identity_sha256:
|
|
739
|
+
raise ValueError("selection draft is bound to a different finite option")
|
|
740
|
+
return option
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
@dataclass(frozen=True, slots=True)
|
|
744
|
+
class InsightDraft:
|
|
745
|
+
claim: str
|
|
746
|
+
trigger: str
|
|
747
|
+
mechanism: str
|
|
748
|
+
affected_paths: Tuple[str, ...]
|
|
749
|
+
evidence_summary: str
|
|
750
|
+
confidence: float
|
|
751
|
+
evidence_contrast_ids: Tuple[str, ...] = ()
|
|
752
|
+
effect_predictions: Tuple[MetricEffectPrediction, ...] = ()
|
|
753
|
+
recommended_option_families: Tuple[str, ...] = ()
|
|
754
|
+
recommended_option_ids: Tuple[str, ...] = ()
|
|
755
|
+
action_template: str | None = None
|
|
756
|
+
falsification_condition: str | None = None
|
|
757
|
+
insight_kind: ReflectionInsightKind | None = None
|
|
758
|
+
consumer_scopes: Tuple[ReflectionConsumerScope, ...] = ()
|
|
759
|
+
factor_capabilities: Tuple[str, ...] = ()
|
|
760
|
+
|
|
761
|
+
def __post_init__(self) -> None:
|
|
762
|
+
for name in ("claim", "trigger", "mechanism", "evidence_summary"):
|
|
763
|
+
value = getattr(self, name)
|
|
764
|
+
if type(value) is not str or not value.strip():
|
|
765
|
+
raise ValueError(f"{name} must be non-empty")
|
|
766
|
+
if type(self.affected_paths) is not tuple or any(
|
|
767
|
+
type(value) is not str or not value.strip() for value in self.affected_paths
|
|
768
|
+
):
|
|
769
|
+
raise TypeError("affected_paths must be a tuple of non-empty strings")
|
|
770
|
+
if (
|
|
771
|
+
isinstance(self.confidence, bool)
|
|
772
|
+
or not isinstance(self.confidence, (int, float))
|
|
773
|
+
or not math.isfinite(float(self.confidence))
|
|
774
|
+
or not 0 <= float(self.confidence) <= 1
|
|
775
|
+
):
|
|
776
|
+
raise ValueError("confidence must be finite in [0,1]")
|
|
777
|
+
_validate_contrast_ids(
|
|
778
|
+
self.evidence_contrast_ids,
|
|
779
|
+
name="evidence_contrast_ids",
|
|
780
|
+
)
|
|
781
|
+
semantic_fields_present = (
|
|
782
|
+
self.insight_kind is not None
|
|
783
|
+
or bool(self.consumer_scopes)
|
|
784
|
+
or bool(self.factor_capabilities)
|
|
785
|
+
or any(
|
|
786
|
+
prediction.comparison_anchor is not None
|
|
787
|
+
for prediction in self.effect_predictions
|
|
788
|
+
if type(prediction) is MetricEffectPrediction
|
|
789
|
+
)
|
|
790
|
+
)
|
|
791
|
+
if semantic_fields_present:
|
|
792
|
+
if type(self.insight_kind) is not ReflectionInsightKind:
|
|
793
|
+
raise TypeError(
|
|
794
|
+
"semantic insights require an exact ReflectionInsightKind"
|
|
795
|
+
)
|
|
796
|
+
_validate_canonical_enum_values(
|
|
797
|
+
self.consumer_scopes,
|
|
798
|
+
name="consumer_scopes",
|
|
799
|
+
enum_type=ReflectionConsumerScope,
|
|
800
|
+
)
|
|
801
|
+
if self.factor_capabilities:
|
|
802
|
+
_validate_canonical_tokens(
|
|
803
|
+
self.factor_capabilities,
|
|
804
|
+
name="factor_capabilities",
|
|
805
|
+
pattern=_REFLECTION_SEMANTIC_TOKEN,
|
|
806
|
+
)
|
|
807
|
+
if (
|
|
808
|
+
self.insight_kind is ReflectionInsightKind.SEARCH_HEURISTIC
|
|
809
|
+
and self.effect_predictions
|
|
810
|
+
):
|
|
811
|
+
raise ValueError(
|
|
812
|
+
"search_heuristic insights cannot carry causal metric predictions"
|
|
813
|
+
)
|
|
814
|
+
elif type(self.consumer_scopes) is not tuple:
|
|
815
|
+
raise TypeError("consumer_scopes must be an exact tuple")
|
|
816
|
+
if type(self.factor_capabilities) is not tuple:
|
|
817
|
+
raise TypeError("factor_capabilities must be an exact tuple")
|
|
818
|
+
advanced_fields_present = (
|
|
819
|
+
bool(self.effect_predictions)
|
|
820
|
+
or bool(self.recommended_option_families)
|
|
821
|
+
or bool(self.recommended_option_ids)
|
|
822
|
+
or self.action_template is not None
|
|
823
|
+
or self.falsification_condition is not None
|
|
824
|
+
)
|
|
825
|
+
if not advanced_fields_present:
|
|
826
|
+
return
|
|
827
|
+
if type(self.effect_predictions) is not tuple or any(
|
|
828
|
+
type(value) is not MetricEffectPrediction
|
|
829
|
+
for value in self.effect_predictions
|
|
830
|
+
):
|
|
831
|
+
raise TypeError(
|
|
832
|
+
"effect_predictions must contain exact MetricEffectPrediction values"
|
|
833
|
+
)
|
|
834
|
+
if not self.effect_predictions:
|
|
835
|
+
raise ValueError("advanced insights require effect_predictions")
|
|
836
|
+
for prediction in self.effect_predictions:
|
|
837
|
+
MetricEffectPrediction.__post_init__(prediction)
|
|
838
|
+
if semantic_fields_present and any(
|
|
839
|
+
prediction.comparison_anchor is None
|
|
840
|
+
for prediction in self.effect_predictions
|
|
841
|
+
):
|
|
842
|
+
raise ValueError(
|
|
843
|
+
"semantic insight effect predictions require comparison anchors"
|
|
844
|
+
)
|
|
845
|
+
metric_ids = tuple(
|
|
846
|
+
prediction.metric_id for prediction in self.effect_predictions
|
|
847
|
+
)
|
|
848
|
+
if metric_ids != tuple(sorted(set(metric_ids))):
|
|
849
|
+
raise ValueError(
|
|
850
|
+
"effect_predictions must be unique and ordered by metric_id"
|
|
851
|
+
)
|
|
852
|
+
_validate_canonical_tokens(
|
|
853
|
+
self.recommended_option_families,
|
|
854
|
+
name="recommended_option_families",
|
|
855
|
+
pattern=_FINITE_OPTION_FAMILY,
|
|
856
|
+
)
|
|
857
|
+
if self.recommended_option_ids:
|
|
858
|
+
_validate_canonical_tokens(
|
|
859
|
+
self.recommended_option_ids,
|
|
860
|
+
name="recommended_option_ids",
|
|
861
|
+
pattern=_FINITE_OPTION_ID,
|
|
862
|
+
)
|
|
863
|
+
for name in ("action_template", "falsification_condition"):
|
|
864
|
+
value = getattr(self, name)
|
|
865
|
+
if type(value) is not str or not value.strip() or value != value.strip():
|
|
866
|
+
raise ValueError(
|
|
867
|
+
f"advanced insights require canonical non-empty {name}"
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
@property
|
|
871
|
+
def has_intervention_contract(self) -> bool:
|
|
872
|
+
"""Whether the opt-in actionable/falsifiable fields are populated."""
|
|
873
|
+
|
|
874
|
+
return bool(self.effect_predictions)
|
|
875
|
+
|
|
876
|
+
@property
|
|
877
|
+
def has_semantic_contract(self) -> bool:
|
|
878
|
+
"""Whether the v3 epistemic/scope vocabulary is populated."""
|
|
879
|
+
|
|
880
|
+
return self.insight_kind is not None
|
|
881
|
+
|
|
882
|
+
def intervention_record(self) -> dict[str, object] | None:
|
|
883
|
+
"""Return a detached JSON-ready projection only for advanced insights."""
|
|
884
|
+
|
|
885
|
+
InsightDraft.__post_init__(self)
|
|
886
|
+
if not self.has_intervention_contract:
|
|
887
|
+
return None
|
|
888
|
+
assert self.action_template is not None
|
|
889
|
+
assert self.falsification_condition is not None
|
|
890
|
+
record: dict[str, object] = {
|
|
891
|
+
"effect_predictions": [
|
|
892
|
+
prediction.to_record() for prediction in self.effect_predictions
|
|
893
|
+
],
|
|
894
|
+
"recommended_option_families": list(self.recommended_option_families),
|
|
895
|
+
"recommended_option_ids": list(self.recommended_option_ids),
|
|
896
|
+
"action_template": self.action_template,
|
|
897
|
+
"falsification_condition": self.falsification_condition,
|
|
898
|
+
}
|
|
899
|
+
semantic = self.semantic_record()
|
|
900
|
+
if semantic is not None:
|
|
901
|
+
record.update(semantic)
|
|
902
|
+
return record
|
|
903
|
+
|
|
904
|
+
def semantic_record(self) -> dict[str, object] | None:
|
|
905
|
+
"""Return v3 semantics independently of intervention actionability.
|
|
906
|
+
|
|
907
|
+
Search heuristics and other outcome schemas may deliberately carry a
|
|
908
|
+
semantic scope without an intervention contract. Keeping this
|
|
909
|
+
projection separate prevents prompt adapters from silently dropping
|
|
910
|
+
the card kind, authorized consumers, or required factor capabilities.
|
|
911
|
+
"""
|
|
912
|
+
|
|
913
|
+
InsightDraft.__post_init__(self)
|
|
914
|
+
if not self.has_semantic_contract:
|
|
915
|
+
return None
|
|
916
|
+
assert self.insight_kind is not None
|
|
917
|
+
return {
|
|
918
|
+
"insight_kind": self.insight_kind.value,
|
|
919
|
+
"consumer_scopes": [scope.value for scope in self.consumer_scopes],
|
|
920
|
+
"factor_capabilities": list(self.factor_capabilities),
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
def content_record(self) -> dict[str, object]:
|
|
924
|
+
"""Return the complete immutable card content used by treatment binding."""
|
|
925
|
+
|
|
926
|
+
InsightDraft.__post_init__(self)
|
|
927
|
+
record: dict[str, object] = {
|
|
928
|
+
"schema_version": 2 if self.has_semantic_contract else 1,
|
|
929
|
+
"claim": self.claim,
|
|
930
|
+
"trigger": self.trigger,
|
|
931
|
+
"mechanism": self.mechanism,
|
|
932
|
+
"affected_paths": list(self.affected_paths),
|
|
933
|
+
"evidence_summary": self.evidence_summary,
|
|
934
|
+
"confidence": float(self.confidence),
|
|
935
|
+
"evidence_contrast_ids": list(self.evidence_contrast_ids),
|
|
936
|
+
"effect_predictions": [
|
|
937
|
+
prediction.to_record() for prediction in self.effect_predictions
|
|
938
|
+
],
|
|
939
|
+
"recommended_option_families": list(self.recommended_option_families),
|
|
940
|
+
"recommended_option_ids": list(self.recommended_option_ids),
|
|
941
|
+
"action_template": self.action_template,
|
|
942
|
+
"falsification_condition": self.falsification_condition,
|
|
943
|
+
}
|
|
944
|
+
if self.has_semantic_contract:
|
|
945
|
+
assert self.insight_kind is not None
|
|
946
|
+
record.update(
|
|
947
|
+
{
|
|
948
|
+
"insight_kind": self.insight_kind.value,
|
|
949
|
+
"consumer_scopes": [scope.value for scope in self.consumer_scopes],
|
|
950
|
+
"factor_capabilities": list(self.factor_capabilities),
|
|
951
|
+
}
|
|
952
|
+
)
|
|
953
|
+
return record
|
|
954
|
+
|
|
955
|
+
def hypothesis_record(self) -> dict[str, object]:
|
|
956
|
+
"""Project model-authored content with its epistemic status explicit.
|
|
957
|
+
|
|
958
|
+
This projection deliberately does not relabel ``evidence_summary`` as an
|
|
959
|
+
observation. It is the model's interpretation of evidence; trusted
|
|
960
|
+
empirical facts belong in an engine-issued evidence snapshot.
|
|
961
|
+
"""
|
|
962
|
+
|
|
963
|
+
InsightDraft.__post_init__(self)
|
|
964
|
+
record: dict[str, object] = {
|
|
965
|
+
"schema_version": 2 if self.has_semantic_contract else 1,
|
|
966
|
+
"epistemic_status": "unverified_hypothesis",
|
|
967
|
+
"claim": self.claim,
|
|
968
|
+
"trigger": self.trigger,
|
|
969
|
+
"mechanism_hypothesis": self.mechanism,
|
|
970
|
+
"affected_paths": list(self.affected_paths),
|
|
971
|
+
"evidence_interpretation": self.evidence_summary,
|
|
972
|
+
"confidence": float(self.confidence),
|
|
973
|
+
"evidence_contrast_ids": list(self.evidence_contrast_ids),
|
|
974
|
+
"effect_predictions": [
|
|
975
|
+
prediction.to_record() for prediction in self.effect_predictions
|
|
976
|
+
],
|
|
977
|
+
"recommended_option_families": list(self.recommended_option_families),
|
|
978
|
+
"recommended_option_ids": list(self.recommended_option_ids),
|
|
979
|
+
"action_template": self.action_template,
|
|
980
|
+
"falsification_condition": self.falsification_condition,
|
|
981
|
+
}
|
|
982
|
+
if self.has_semantic_contract:
|
|
983
|
+
assert self.insight_kind is not None
|
|
984
|
+
record.update(
|
|
985
|
+
{
|
|
986
|
+
"insight_kind": self.insight_kind.value,
|
|
987
|
+
"consumer_scopes": [scope.value for scope in self.consumer_scopes],
|
|
988
|
+
"factor_capabilities": list(self.factor_capabilities),
|
|
989
|
+
}
|
|
990
|
+
)
|
|
991
|
+
return record
|
|
992
|
+
|
|
993
|
+
@property
|
|
994
|
+
def hypothesis_sha256(self) -> str:
|
|
995
|
+
"""Bind the explicitly unverified hypothesis projection."""
|
|
996
|
+
|
|
997
|
+
return hashlib.sha256(
|
|
998
|
+
_INSIGHT_DRAFT_HYPOTHESIS_DOMAIN
|
|
999
|
+
+ canonical_typed_json_bytes(freeze_json(self.hypothesis_record()))
|
|
1000
|
+
).hexdigest()
|
|
1001
|
+
|
|
1002
|
+
@property
|
|
1003
|
+
def content_sha256(self) -> str:
|
|
1004
|
+
"""Bind prose, evidence, predictions, and exact finite actions."""
|
|
1005
|
+
|
|
1006
|
+
return hashlib.sha256(
|
|
1007
|
+
_INSIGHT_DRAFT_CONTENT_DOMAIN
|
|
1008
|
+
+ canonical_typed_json_bytes(freeze_json(self.content_record()))
|
|
1009
|
+
).hexdigest()
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
@dataclass(frozen=True, slots=True)
|
|
1013
|
+
class ReflectionInsightContract:
|
|
1014
|
+
"""Request-scoped vocabulary for actionable, falsifiable reflection.
|
|
1015
|
+
|
|
1016
|
+
Metric identifiers are benchmark-owned and name quantities whose numeric
|
|
1017
|
+
direction can be adjudicated later. Option families are drawn from the
|
|
1018
|
+
finite variation vocabulary and remain stable when parent-specific option
|
|
1019
|
+
IDs change. Keeping this contract request-scoped avoids imposing any one
|
|
1020
|
+
benchmark's metrics or action taxonomy on the core workflow.
|
|
1021
|
+
"""
|
|
1022
|
+
|
|
1023
|
+
required_metric_ids: Tuple[str, ...]
|
|
1024
|
+
allowed_option_families: Tuple[str, ...]
|
|
1025
|
+
allowed_option_ids: Tuple[str, ...] = ()
|
|
1026
|
+
allowed_decision_paths: Tuple[str, ...] = ()
|
|
1027
|
+
allowed_insight_kinds: Tuple[ReflectionInsightKind, ...] = ()
|
|
1028
|
+
allowed_consumer_scopes: Tuple[ReflectionConsumerScope, ...] = ()
|
|
1029
|
+
allowed_comparison_anchor_kinds: Tuple[MetricComparisonAnchorKind, ...] = ()
|
|
1030
|
+
allowed_factor_capabilities: Tuple[str, ...] = ()
|
|
1031
|
+
allowed_source_role_ids: Tuple[str, ...] = ()
|
|
1032
|
+
|
|
1033
|
+
def __post_init__(self) -> None:
|
|
1034
|
+
_validate_canonical_tokens(
|
|
1035
|
+
self.required_metric_ids,
|
|
1036
|
+
name="required_metric_ids",
|
|
1037
|
+
pattern=_REFLECTION_METRIC_ID,
|
|
1038
|
+
)
|
|
1039
|
+
_validate_canonical_tokens(
|
|
1040
|
+
self.allowed_option_families,
|
|
1041
|
+
name="allowed_option_families",
|
|
1042
|
+
pattern=_FINITE_OPTION_FAMILY,
|
|
1043
|
+
)
|
|
1044
|
+
if self.allowed_option_ids:
|
|
1045
|
+
_validate_canonical_tokens(
|
|
1046
|
+
self.allowed_option_ids,
|
|
1047
|
+
name="allowed_option_ids",
|
|
1048
|
+
pattern=_FINITE_OPTION_ID,
|
|
1049
|
+
)
|
|
1050
|
+
for name in (
|
|
1051
|
+
"allowed_decision_paths",
|
|
1052
|
+
"allowed_insight_kinds",
|
|
1053
|
+
"allowed_consumer_scopes",
|
|
1054
|
+
"allowed_comparison_anchor_kinds",
|
|
1055
|
+
"allowed_factor_capabilities",
|
|
1056
|
+
"allowed_source_role_ids",
|
|
1057
|
+
):
|
|
1058
|
+
if type(getattr(self, name)) is not tuple:
|
|
1059
|
+
raise TypeError(f"{name} must be an exact tuple")
|
|
1060
|
+
semantic_fields_present = any(
|
|
1061
|
+
(
|
|
1062
|
+
self.allowed_decision_paths,
|
|
1063
|
+
self.allowed_insight_kinds,
|
|
1064
|
+
self.allowed_consumer_scopes,
|
|
1065
|
+
self.allowed_comparison_anchor_kinds,
|
|
1066
|
+
self.allowed_factor_capabilities,
|
|
1067
|
+
self.allowed_source_role_ids,
|
|
1068
|
+
)
|
|
1069
|
+
)
|
|
1070
|
+
if not semantic_fields_present:
|
|
1071
|
+
return
|
|
1072
|
+
_validate_decision_paths(
|
|
1073
|
+
self.allowed_decision_paths,
|
|
1074
|
+
name="allowed_decision_paths",
|
|
1075
|
+
)
|
|
1076
|
+
_validate_canonical_enum_values(
|
|
1077
|
+
self.allowed_insight_kinds,
|
|
1078
|
+
name="allowed_insight_kinds",
|
|
1079
|
+
enum_type=ReflectionInsightKind,
|
|
1080
|
+
)
|
|
1081
|
+
_validate_canonical_enum_values(
|
|
1082
|
+
self.allowed_consumer_scopes,
|
|
1083
|
+
name="allowed_consumer_scopes",
|
|
1084
|
+
enum_type=ReflectionConsumerScope,
|
|
1085
|
+
)
|
|
1086
|
+
_validate_canonical_enum_values(
|
|
1087
|
+
self.allowed_comparison_anchor_kinds,
|
|
1088
|
+
name="allowed_comparison_anchor_kinds",
|
|
1089
|
+
enum_type=MetricComparisonAnchorKind,
|
|
1090
|
+
)
|
|
1091
|
+
if ReflectionInsightKind.SEARCH_HEURISTIC in self.allowed_insight_kinds:
|
|
1092
|
+
raise ValueError(
|
|
1093
|
+
"actionable reflection contracts cannot admit search_heuristic "
|
|
1094
|
+
"without a separate outcome schema"
|
|
1095
|
+
)
|
|
1096
|
+
if ReflectionInsightKind.CONTRACT_INVARIANT in self.allowed_insight_kinds:
|
|
1097
|
+
raise ValueError(
|
|
1098
|
+
"model-authored actionable reflection contracts cannot admit "
|
|
1099
|
+
"contract_invariant"
|
|
1100
|
+
)
|
|
1101
|
+
if self.allowed_factor_capabilities:
|
|
1102
|
+
_validate_canonical_tokens(
|
|
1103
|
+
self.allowed_factor_capabilities,
|
|
1104
|
+
name="allowed_factor_capabilities",
|
|
1105
|
+
pattern=_REFLECTION_SEMANTIC_TOKEN,
|
|
1106
|
+
)
|
|
1107
|
+
if (
|
|
1108
|
+
len(self.allowed_factor_capabilities)
|
|
1109
|
+
> MAX_REFLECTION_SEMANTIC_VOCABULARY_ENTRIES
|
|
1110
|
+
):
|
|
1111
|
+
raise ValueError(
|
|
1112
|
+
"allowed_factor_capabilities exceeds the semantic vocabulary cap"
|
|
1113
|
+
)
|
|
1114
|
+
elif type(self.allowed_factor_capabilities) is not tuple:
|
|
1115
|
+
raise TypeError("allowed_factor_capabilities must be an exact tuple")
|
|
1116
|
+
if self.allowed_source_role_ids:
|
|
1117
|
+
_validate_canonical_tokens(
|
|
1118
|
+
self.allowed_source_role_ids,
|
|
1119
|
+
name="allowed_source_role_ids",
|
|
1120
|
+
pattern=_REFLECTION_SEMANTIC_TOKEN,
|
|
1121
|
+
)
|
|
1122
|
+
if (
|
|
1123
|
+
len(self.allowed_source_role_ids)
|
|
1124
|
+
> MAX_REFLECTION_SEMANTIC_VOCABULARY_ENTRIES
|
|
1125
|
+
):
|
|
1126
|
+
raise ValueError(
|
|
1127
|
+
"allowed_source_role_ids exceeds the semantic vocabulary cap"
|
|
1128
|
+
)
|
|
1129
|
+
elif type(self.allowed_source_role_ids) is not tuple:
|
|
1130
|
+
raise TypeError("allowed_source_role_ids must be an exact tuple")
|
|
1131
|
+
named_role_allowed = (
|
|
1132
|
+
MetricComparisonAnchorKind.NAMED_SOURCE_ROLE
|
|
1133
|
+
in self.allowed_comparison_anchor_kinds
|
|
1134
|
+
)
|
|
1135
|
+
if named_role_allowed and not self.allowed_source_role_ids:
|
|
1136
|
+
raise ValueError(
|
|
1137
|
+
"named_source_role anchors require adapter-owned source role IDs"
|
|
1138
|
+
)
|
|
1139
|
+
if self.allowed_source_role_ids and not named_role_allowed:
|
|
1140
|
+
raise ValueError(
|
|
1141
|
+
"source role IDs require named_source_role in the anchor vocabulary"
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1144
|
+
@property
|
|
1145
|
+
def is_semantic_v3(self) -> bool:
|
|
1146
|
+
"""Whether this request opts into the fail-closed semantic contract."""
|
|
1147
|
+
|
|
1148
|
+
return bool(self.allowed_decision_paths)
|
|
1149
|
+
|
|
1150
|
+
def to_record(self) -> dict[str, object]:
|
|
1151
|
+
ReflectionInsightContract.__post_init__(self)
|
|
1152
|
+
record: dict[str, object] = {
|
|
1153
|
+
"schema_version": 3 if self.is_semantic_v3 else 2,
|
|
1154
|
+
"contract_identity_sha256": self.identity_sha256,
|
|
1155
|
+
"required_metric_ids": list(self.required_metric_ids),
|
|
1156
|
+
"allowed_option_families": list(self.allowed_option_families),
|
|
1157
|
+
"allowed_option_ids": list(self.allowed_option_ids),
|
|
1158
|
+
"direction_vocabulary": [
|
|
1159
|
+
direction.value for direction in MetricEffectDirection
|
|
1160
|
+
],
|
|
1161
|
+
}
|
|
1162
|
+
if self.is_semantic_v3:
|
|
1163
|
+
record.update(
|
|
1164
|
+
{
|
|
1165
|
+
"allowed_decision_paths": list(self.allowed_decision_paths),
|
|
1166
|
+
"allowed_insight_kinds": [
|
|
1167
|
+
kind.value for kind in self.allowed_insight_kinds
|
|
1168
|
+
],
|
|
1169
|
+
"allowed_consumer_scopes": [
|
|
1170
|
+
scope.value for scope in self.allowed_consumer_scopes
|
|
1171
|
+
],
|
|
1172
|
+
"allowed_comparison_anchor_kinds": [
|
|
1173
|
+
kind.value for kind in self.allowed_comparison_anchor_kinds
|
|
1174
|
+
],
|
|
1175
|
+
"allowed_factor_capabilities": list(
|
|
1176
|
+
self.allowed_factor_capabilities
|
|
1177
|
+
),
|
|
1178
|
+
"allowed_source_role_ids": list(self.allowed_source_role_ids),
|
|
1179
|
+
}
|
|
1180
|
+
)
|
|
1181
|
+
return record
|
|
1182
|
+
|
|
1183
|
+
@property
|
|
1184
|
+
def identity_sha256(self) -> str:
|
|
1185
|
+
"""Bind exact ordered metric/action vocabularies for replay."""
|
|
1186
|
+
|
|
1187
|
+
ReflectionInsightContract.__post_init__(self)
|
|
1188
|
+
record: dict[str, object] = {
|
|
1189
|
+
"schema_version": 3 if self.is_semantic_v3 else 2,
|
|
1190
|
+
"required_metric_ids": list(self.required_metric_ids),
|
|
1191
|
+
"allowed_option_families": list(self.allowed_option_families),
|
|
1192
|
+
"allowed_option_ids": list(self.allowed_option_ids),
|
|
1193
|
+
"direction_vocabulary": [
|
|
1194
|
+
direction.value for direction in MetricEffectDirection
|
|
1195
|
+
],
|
|
1196
|
+
}
|
|
1197
|
+
if self.is_semantic_v3:
|
|
1198
|
+
record.update(
|
|
1199
|
+
{
|
|
1200
|
+
"allowed_decision_paths": list(self.allowed_decision_paths),
|
|
1201
|
+
"allowed_insight_kinds": [
|
|
1202
|
+
kind.value for kind in self.allowed_insight_kinds
|
|
1203
|
+
],
|
|
1204
|
+
"allowed_consumer_scopes": [
|
|
1205
|
+
scope.value for scope in self.allowed_consumer_scopes
|
|
1206
|
+
],
|
|
1207
|
+
"allowed_comparison_anchor_kinds": [
|
|
1208
|
+
kind.value for kind in self.allowed_comparison_anchor_kinds
|
|
1209
|
+
],
|
|
1210
|
+
"allowed_factor_capabilities": list(
|
|
1211
|
+
self.allowed_factor_capabilities
|
|
1212
|
+
),
|
|
1213
|
+
"allowed_source_role_ids": list(self.allowed_source_role_ids),
|
|
1214
|
+
}
|
|
1215
|
+
)
|
|
1216
|
+
payload = freeze_json(record)
|
|
1217
|
+
return hashlib.sha256(
|
|
1218
|
+
(
|
|
1219
|
+
_REFLECTION_INSIGHT_CONTRACT_V3_DOMAIN
|
|
1220
|
+
if self.is_semantic_v3
|
|
1221
|
+
else _REFLECTION_INSIGHT_CONTRACT_DOMAIN
|
|
1222
|
+
)
|
|
1223
|
+
+ canonical_typed_json_bytes(payload)
|
|
1224
|
+
).hexdigest()
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
def validate_reflection_insight_draft(
|
|
1228
|
+
draft: InsightDraft,
|
|
1229
|
+
contract: ReflectionInsightContract,
|
|
1230
|
+
*,
|
|
1231
|
+
allow_all_unknown: bool = False,
|
|
1232
|
+
allow_missing_evidence: bool = False,
|
|
1233
|
+
) -> None:
|
|
1234
|
+
"""Verify exact metric coverage and finite-vocabulary actionability."""
|
|
1235
|
+
|
|
1236
|
+
if type(allow_all_unknown) is not bool:
|
|
1237
|
+
raise TypeError("allow_all_unknown must be an exact boolean")
|
|
1238
|
+
if type(allow_missing_evidence) is not bool:
|
|
1239
|
+
raise TypeError("allow_missing_evidence must be an exact boolean")
|
|
1240
|
+
if type(draft) is not InsightDraft:
|
|
1241
|
+
raise TypeError("draft must be an exact InsightDraft")
|
|
1242
|
+
InsightDraft.__post_init__(draft)
|
|
1243
|
+
if type(contract) is not ReflectionInsightContract:
|
|
1244
|
+
raise TypeError("contract must be an exact ReflectionInsightContract")
|
|
1245
|
+
ReflectionInsightContract.__post_init__(contract)
|
|
1246
|
+
if not draft.has_intervention_contract:
|
|
1247
|
+
raise ValueError("insight is missing the advanced intervention contract")
|
|
1248
|
+
if contract.is_semantic_v3:
|
|
1249
|
+
if not draft.has_semantic_contract:
|
|
1250
|
+
raise ValueError("v3 insight is missing its semantic contract")
|
|
1251
|
+
assert draft.insight_kind is not None
|
|
1252
|
+
if draft.insight_kind is ReflectionInsightKind.CONTRACT_INVARIANT:
|
|
1253
|
+
raise ValueError(
|
|
1254
|
+
"model-authored v3 reflections cannot assert contract_invariant"
|
|
1255
|
+
)
|
|
1256
|
+
if not draft.affected_paths or len(set(draft.affected_paths)) != len(
|
|
1257
|
+
draft.affected_paths
|
|
1258
|
+
):
|
|
1259
|
+
raise ValueError("v3 affected paths must be nonempty and unique")
|
|
1260
|
+
if draft.insight_kind not in contract.allowed_insight_kinds:
|
|
1261
|
+
raise ValueError("insight kind escapes the request vocabulary")
|
|
1262
|
+
if not set(draft.consumer_scopes).issubset(contract.allowed_consumer_scopes):
|
|
1263
|
+
raise ValueError("consumer scopes escape the request vocabulary")
|
|
1264
|
+
if not set(draft.affected_paths).issubset(contract.allowed_decision_paths):
|
|
1265
|
+
raise ValueError(
|
|
1266
|
+
"affected paths escape the adapter-owned decision-path vocabulary"
|
|
1267
|
+
)
|
|
1268
|
+
if not set(draft.factor_capabilities).issubset(
|
|
1269
|
+
contract.allowed_factor_capabilities
|
|
1270
|
+
):
|
|
1271
|
+
raise ValueError(
|
|
1272
|
+
"factor capabilities escape the adapter-owned capability vocabulary"
|
|
1273
|
+
)
|
|
1274
|
+
for prediction in draft.effect_predictions:
|
|
1275
|
+
if prediction.direction is MetricEffectDirection.UNKNOWN:
|
|
1276
|
+
raise ValueError(
|
|
1277
|
+
"v3 lifecycle hypotheses require adjudicable metric directions"
|
|
1278
|
+
)
|
|
1279
|
+
anchor = prediction.comparison_anchor
|
|
1280
|
+
if anchor is None:
|
|
1281
|
+
raise ValueError(
|
|
1282
|
+
"v3 effect predictions require an explicit comparison anchor"
|
|
1283
|
+
)
|
|
1284
|
+
if anchor.kind not in contract.allowed_comparison_anchor_kinds:
|
|
1285
|
+
raise ValueError(
|
|
1286
|
+
"comparison anchor kind escapes the request vocabulary"
|
|
1287
|
+
)
|
|
1288
|
+
if (
|
|
1289
|
+
anchor.source_role_id is not None
|
|
1290
|
+
and anchor.source_role_id not in contract.allowed_source_role_ids
|
|
1291
|
+
):
|
|
1292
|
+
raise ValueError(
|
|
1293
|
+
"comparison source role escapes the request vocabulary"
|
|
1294
|
+
)
|
|
1295
|
+
elif draft.has_semantic_contract:
|
|
1296
|
+
raise ValueError("a v3 semantic insight requires a v3 reflection contract")
|
|
1297
|
+
if not allow_missing_evidence and not draft.evidence_contrast_ids:
|
|
1298
|
+
raise ValueError(
|
|
1299
|
+
"an outcome-grounded intervention must cite at least one evidence contrast"
|
|
1300
|
+
)
|
|
1301
|
+
predicted_metric_ids = tuple(
|
|
1302
|
+
prediction.metric_id for prediction in draft.effect_predictions
|
|
1303
|
+
)
|
|
1304
|
+
if predicted_metric_ids != contract.required_metric_ids:
|
|
1305
|
+
raise ValueError(
|
|
1306
|
+
"effect predictions must cover the exact required metric identifiers"
|
|
1307
|
+
)
|
|
1308
|
+
if not set(draft.recommended_option_families).issubset(
|
|
1309
|
+
contract.allowed_option_families
|
|
1310
|
+
):
|
|
1311
|
+
raise ValueError(
|
|
1312
|
+
"recommended option families escape the finite action vocabulary"
|
|
1313
|
+
)
|
|
1314
|
+
if contract.allowed_option_ids:
|
|
1315
|
+
if not draft.recommended_option_ids:
|
|
1316
|
+
raise ValueError(
|
|
1317
|
+
"an exact-action insight must recommend at least one option ID"
|
|
1318
|
+
)
|
|
1319
|
+
if not set(draft.recommended_option_ids).issubset(contract.allowed_option_ids):
|
|
1320
|
+
raise ValueError(
|
|
1321
|
+
"recommended option IDs escape the finite action vocabulary"
|
|
1322
|
+
)
|
|
1323
|
+
elif draft.recommended_option_ids:
|
|
1324
|
+
raise ValueError(
|
|
1325
|
+
"recommended option IDs require a request-scoped exact-action vocabulary"
|
|
1326
|
+
)
|
|
1327
|
+
if not allow_all_unknown and all(
|
|
1328
|
+
prediction.direction is MetricEffectDirection.UNKNOWN
|
|
1329
|
+
for prediction in draft.effect_predictions
|
|
1330
|
+
):
|
|
1331
|
+
raise ValueError(
|
|
1332
|
+
"an outcome-grounded intervention must make a directional prediction"
|
|
1333
|
+
)
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
@dataclass(frozen=True, slots=True)
|
|
1337
|
+
class AgenticCallTelemetry:
|
|
1338
|
+
requested_model: str
|
|
1339
|
+
resolved_model: str
|
|
1340
|
+
resolved_provider: str
|
|
1341
|
+
provider_response_id: str | None
|
|
1342
|
+
finish_reason: str | None
|
|
1343
|
+
input_tokens: int
|
|
1344
|
+
output_tokens: int
|
|
1345
|
+
reasoning_tokens: int
|
|
1346
|
+
cache_read_tokens: int
|
|
1347
|
+
cache_write_tokens: int
|
|
1348
|
+
cost_usd: Decimal | None
|
|
1349
|
+
latency_ns: int
|
|
1350
|
+
attempt_count: int = 1
|
|
1351
|
+
|
|
1352
|
+
def __post_init__(self) -> None:
|
|
1353
|
+
for name in ("requested_model", "resolved_model", "resolved_provider"):
|
|
1354
|
+
if type(getattr(self, name)) is not str or not getattr(self, name).strip():
|
|
1355
|
+
raise ValueError(f"{name} must be non-empty")
|
|
1356
|
+
for name in (
|
|
1357
|
+
"input_tokens",
|
|
1358
|
+
"output_tokens",
|
|
1359
|
+
"reasoning_tokens",
|
|
1360
|
+
"cache_read_tokens",
|
|
1361
|
+
"cache_write_tokens",
|
|
1362
|
+
"latency_ns",
|
|
1363
|
+
):
|
|
1364
|
+
if type(getattr(self, name)) is not int or getattr(self, name) < 0:
|
|
1365
|
+
raise ValueError(f"{name} must be a non-negative integer")
|
|
1366
|
+
if type(self.attempt_count) is not int or self.attempt_count <= 0:
|
|
1367
|
+
raise ValueError("attempt_count must be positive")
|
|
1368
|
+
|
|
1369
|
+
|
|
1370
|
+
@dataclass(frozen=True, slots=True)
|
|
1371
|
+
class VariationGenerationRequest:
|
|
1372
|
+
call_id: LLMCallId
|
|
1373
|
+
operation: str
|
|
1374
|
+
prompt: str
|
|
1375
|
+
candidate_model: type
|
|
1376
|
+
max_output_tokens: int = 2_048
|
|
1377
|
+
temperature: float | None = None
|
|
1378
|
+
atomic_mutation_contract: AtomicMutationOutputContract | None = None
|
|
1379
|
+
finite_variation_contract: FiniteVariationContract | None = None
|
|
1380
|
+
exact_parent_crossover_contract: ExactParentCrossoverOutputContract | None = None
|
|
1381
|
+
|
|
1382
|
+
def __post_init__(self) -> None:
|
|
1383
|
+
contracts = tuple(
|
|
1384
|
+
value
|
|
1385
|
+
for value in (
|
|
1386
|
+
self.atomic_mutation_contract,
|
|
1387
|
+
self.finite_variation_contract,
|
|
1388
|
+
self.exact_parent_crossover_contract,
|
|
1389
|
+
)
|
|
1390
|
+
if value is not None
|
|
1391
|
+
)
|
|
1392
|
+
if len(contracts) > 1:
|
|
1393
|
+
raise ValueError("variation output contracts are mutually exclusive")
|
|
1394
|
+
if self.atomic_mutation_contract is not None:
|
|
1395
|
+
if type(self.atomic_mutation_contract) is not AtomicMutationOutputContract:
|
|
1396
|
+
raise TypeError(
|
|
1397
|
+
"atomic_mutation_contract must be an exact "
|
|
1398
|
+
"AtomicMutationOutputContract"
|
|
1399
|
+
)
|
|
1400
|
+
AtomicMutationOutputContract.__post_init__(self.atomic_mutation_contract)
|
|
1401
|
+
if self.finite_variation_contract is not None:
|
|
1402
|
+
validate_finite_variation_contract(self.finite_variation_contract)
|
|
1403
|
+
if self.exact_parent_crossover_contract is not None:
|
|
1404
|
+
if (
|
|
1405
|
+
type(self.exact_parent_crossover_contract)
|
|
1406
|
+
is not ExactParentCrossoverOutputContract
|
|
1407
|
+
):
|
|
1408
|
+
raise TypeError("exact_parent_crossover_contract must be exact")
|
|
1409
|
+
ExactParentCrossoverOutputContract.__post_init__(
|
|
1410
|
+
self.exact_parent_crossover_contract
|
|
1411
|
+
)
|
|
1412
|
+
if self.operation != "two_parent_crossover":
|
|
1413
|
+
raise ValueError(
|
|
1414
|
+
"exact parent crossover output is restricted to "
|
|
1415
|
+
"two_parent_crossover"
|
|
1416
|
+
)
|
|
1417
|
+
|
|
1418
|
+
|
|
1419
|
+
@dataclass(frozen=True, slots=True)
|
|
1420
|
+
class ReflectionGenerationRequest:
|
|
1421
|
+
call_id: LLMCallId
|
|
1422
|
+
operation: str
|
|
1423
|
+
prompt: str
|
|
1424
|
+
max_insights: int = 4
|
|
1425
|
+
min_insights: int = 0
|
|
1426
|
+
max_output_tokens: int = 2_048
|
|
1427
|
+
temperature: float | None = None
|
|
1428
|
+
available_contrast_ids: Tuple[str, ...] = ()
|
|
1429
|
+
insight_contract: ReflectionInsightContract | None = None
|
|
1430
|
+
evidence_catalog: ReflectionEvidenceCatalog | None = None
|
|
1431
|
+
|
|
1432
|
+
def __post_init__(self) -> None:
|
|
1433
|
+
if type(self.max_insights) is not int or not 1 <= self.max_insights <= 16:
|
|
1434
|
+
raise ValueError("max_insights must lie in [1,16]")
|
|
1435
|
+
if (
|
|
1436
|
+
type(self.min_insights) is not int
|
|
1437
|
+
or not 0 <= self.min_insights <= self.max_insights
|
|
1438
|
+
):
|
|
1439
|
+
raise ValueError("min_insights must lie in [0,max_insights]")
|
|
1440
|
+
_validate_contrast_ids(
|
|
1441
|
+
self.available_contrast_ids,
|
|
1442
|
+
name="available_contrast_ids",
|
|
1443
|
+
)
|
|
1444
|
+
if self.evidence_catalog is not None:
|
|
1445
|
+
if type(self.evidence_catalog) is not ReflectionEvidenceCatalog:
|
|
1446
|
+
raise TypeError(
|
|
1447
|
+
"evidence_catalog must be an exact ReflectionEvidenceCatalog or None"
|
|
1448
|
+
)
|
|
1449
|
+
ReflectionEvidenceCatalog.__post_init__(self.evidence_catalog)
|
|
1450
|
+
if self.evidence_catalog.contrast_ids != self.available_contrast_ids:
|
|
1451
|
+
raise ValueError(
|
|
1452
|
+
"evidence_catalog must bind the exact available_contrast_ids"
|
|
1453
|
+
)
|
|
1454
|
+
if self.insight_contract is not None:
|
|
1455
|
+
if type(self.insight_contract) is not ReflectionInsightContract:
|
|
1456
|
+
raise TypeError(
|
|
1457
|
+
"insight_contract must be an exact ReflectionInsightContract"
|
|
1458
|
+
)
|
|
1459
|
+
ReflectionInsightContract.__post_init__(self.insight_contract)
|
|
1460
|
+
|
|
1461
|
+
|
|
1462
|
+
@dataclass(frozen=True, slots=True)
|
|
1463
|
+
class VariationGenerationResult:
|
|
1464
|
+
draft: (
|
|
1465
|
+
CandidateDraft
|
|
1466
|
+
| AtomicMutationDraft
|
|
1467
|
+
| FiniteVariationSelectionDraft
|
|
1468
|
+
| ExactParentCrossoverDraft
|
|
1469
|
+
)
|
|
1470
|
+
telemetry: AgenticCallTelemetry
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
@dataclass(frozen=True, slots=True)
|
|
1474
|
+
class ReflectionGenerationResult:
|
|
1475
|
+
insights: Tuple[InsightDraft, ...]
|
|
1476
|
+
telemetry: AgenticCallTelemetry
|
|
1477
|
+
evidence_catalog_identity_sha256: str | None = None
|
|
1478
|
+
|
|
1479
|
+
def __post_init__(self) -> None:
|
|
1480
|
+
if self.evidence_catalog_identity_sha256 is not None:
|
|
1481
|
+
require_sha256(
|
|
1482
|
+
self.evidence_catalog_identity_sha256,
|
|
1483
|
+
"evidence_catalog_identity_sha256",
|
|
1484
|
+
)
|
|
1485
|
+
|
|
1486
|
+
|
|
1487
|
+
def validate_reflection_evidence_catalog_result(
|
|
1488
|
+
request: ReflectionGenerationRequest,
|
|
1489
|
+
result: ReflectionGenerationResult,
|
|
1490
|
+
) -> None:
|
|
1491
|
+
"""Bind resolved full-ID citations and catalog identity to one request."""
|
|
1492
|
+
|
|
1493
|
+
if type(request) is not ReflectionGenerationRequest:
|
|
1494
|
+
raise TypeError("request must be an exact ReflectionGenerationRequest")
|
|
1495
|
+
ReflectionGenerationRequest.__post_init__(request)
|
|
1496
|
+
if type(result) is not ReflectionGenerationResult:
|
|
1497
|
+
raise TypeError("result must be an exact ReflectionGenerationResult")
|
|
1498
|
+
ReflectionGenerationResult.__post_init__(result)
|
|
1499
|
+
if type(result.insights) is not tuple or any(
|
|
1500
|
+
type(insight) is not InsightDraft for insight in result.insights
|
|
1501
|
+
):
|
|
1502
|
+
raise TypeError("result insights must contain exact InsightDraft values")
|
|
1503
|
+
available = set(request.available_contrast_ids)
|
|
1504
|
+
for insight in result.insights:
|
|
1505
|
+
InsightDraft.__post_init__(insight)
|
|
1506
|
+
if not set(insight.evidence_contrast_ids).issubset(available):
|
|
1507
|
+
raise ValueError("resolved reflection citations escaped the request")
|
|
1508
|
+
expected_identity = (
|
|
1509
|
+
None
|
|
1510
|
+
if request.evidence_catalog is None
|
|
1511
|
+
else request.evidence_catalog.catalog_identity_sha256
|
|
1512
|
+
)
|
|
1513
|
+
if result.evidence_catalog_identity_sha256 != expected_identity:
|
|
1514
|
+
raise ValueError("reflection result has a foreign evidence catalog identity")
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
@runtime_checkable
|
|
1518
|
+
class AgenticGenerator(Protocol):
|
|
1519
|
+
async def propose(
|
|
1520
|
+
self, request: VariationGenerationRequest
|
|
1521
|
+
) -> VariationGenerationResult: ...
|
|
1522
|
+
|
|
1523
|
+
async def reflect(
|
|
1524
|
+
self, request: ReflectionGenerationRequest
|
|
1525
|
+
) -> ReflectionGenerationResult: ...
|
|
1526
|
+
|
|
1527
|
+
|
|
1528
|
+
__all__ = [
|
|
1529
|
+
"AgenticCallTelemetry",
|
|
1530
|
+
"AgenticGenerator",
|
|
1531
|
+
"AtomicMutationDraft",
|
|
1532
|
+
"AtomicMutationOutputContract",
|
|
1533
|
+
"CANDIDATE_COMPONENT_PATH_CONTRACT",
|
|
1534
|
+
"TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT",
|
|
1535
|
+
"CandidateDraft",
|
|
1536
|
+
"ConflictResolutionDraft",
|
|
1537
|
+
"ExactParentCrossoverDraft",
|
|
1538
|
+
"ExactParentCrossoverOutputContract",
|
|
1539
|
+
"FiniteVariationSelectionDraft",
|
|
1540
|
+
"InsightDraft",
|
|
1541
|
+
"MetricComparisonAnchor",
|
|
1542
|
+
"MetricComparisonAnchorKind",
|
|
1543
|
+
"MetricEffectDirection",
|
|
1544
|
+
"MetricEffectPrediction",
|
|
1545
|
+
"MAX_REFLECTION_EVIDENCE_CATALOG_ENTRIES",
|
|
1546
|
+
"MAX_REFLECTION_SEMANTIC_VOCABULARY_ENTRIES",
|
|
1547
|
+
"MAX_REFLECTION_DECISION_PATH_CHARS",
|
|
1548
|
+
"ReflectionEvidenceCatalog",
|
|
1549
|
+
"ReflectionEvidenceCatalogEntry",
|
|
1550
|
+
"ReflectionGenerationRequest",
|
|
1551
|
+
"ReflectionGenerationResult",
|
|
1552
|
+
"ReflectionConsumerScope",
|
|
1553
|
+
"ReflectionInsightContract",
|
|
1554
|
+
"ReflectionInsightKind",
|
|
1555
|
+
"resolve_finite_variation_selection",
|
|
1556
|
+
"SourceAttribution",
|
|
1557
|
+
"VariationGenerationRequest",
|
|
1558
|
+
"VariationGenerationResult",
|
|
1559
|
+
"validate_reflection_insight_draft",
|
|
1560
|
+
"validate_reflection_evidence_catalog_result",
|
|
1561
|
+
]
|