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,2308 @@
|
|
|
1
|
+
"""High-level agentic generation over the provider-neutral structured port.
|
|
2
|
+
|
|
3
|
+
This module owns only schema construction and translation. The injected
|
|
4
|
+
callable owns execution (and may itself be backed by a retrying queue), so this
|
|
5
|
+
adapter performs no provider setup, I/O, retrying, or exception translation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import copy
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
from collections.abc import Awaitable, Callable
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from typing import Annotated, Any, ClassVar, Generic, Literal, TypeVar, cast, get_args
|
|
16
|
+
|
|
17
|
+
from pydantic import (
|
|
18
|
+
BaseModel,
|
|
19
|
+
ConfigDict,
|
|
20
|
+
Field,
|
|
21
|
+
StringConstraints,
|
|
22
|
+
create_model,
|
|
23
|
+
field_validator,
|
|
24
|
+
model_validator,
|
|
25
|
+
)
|
|
26
|
+
from pydantic_core import PydanticCustomError
|
|
27
|
+
|
|
28
|
+
from agent_evolve.domain.llm_task_queue import ValidationIssueReasonCode
|
|
29
|
+
from agent_evolve.domain.patch import ArrayIndex, JsonPath, ObjectKey
|
|
30
|
+
from agent_evolve.domain.finite_variation import (
|
|
31
|
+
FiniteVariationContract,
|
|
32
|
+
validate_finite_variation_contract,
|
|
33
|
+
)
|
|
34
|
+
from agent_evolve.domain.typed_json import (
|
|
35
|
+
FrozenJsonValue,
|
|
36
|
+
freeze_json,
|
|
37
|
+
is_json_scalar,
|
|
38
|
+
thaw_json,
|
|
39
|
+
typed_json_equal,
|
|
40
|
+
)
|
|
41
|
+
from agent_evolve.policies.variation.typed_patch import (
|
|
42
|
+
replace_existing_path,
|
|
43
|
+
value_at_path,
|
|
44
|
+
)
|
|
45
|
+
from agent_evolve.ports.agentic_generator import (
|
|
46
|
+
AgenticCallTelemetry,
|
|
47
|
+
AtomicMutationDraft,
|
|
48
|
+
AtomicMutationOutputContract,
|
|
49
|
+
CANDIDATE_COMPONENT_PATH_CONTRACT,
|
|
50
|
+
CandidateDraft,
|
|
51
|
+
ConflictResolutionDraft,
|
|
52
|
+
ExactParentCrossoverDraft,
|
|
53
|
+
ExactParentCrossoverOutputContract,
|
|
54
|
+
FiniteVariationSelectionDraft,
|
|
55
|
+
InsightDraft,
|
|
56
|
+
MetricComparisonAnchor,
|
|
57
|
+
MetricComparisonAnchorKind,
|
|
58
|
+
MetricEffectDirection,
|
|
59
|
+
MetricEffectPrediction,
|
|
60
|
+
ReflectionConsumerScope,
|
|
61
|
+
ReflectionEvidenceCatalog,
|
|
62
|
+
ReflectionGenerationRequest,
|
|
63
|
+
ReflectionGenerationResult,
|
|
64
|
+
ReflectionInsightContract,
|
|
65
|
+
ReflectionInsightKind,
|
|
66
|
+
SourceAttribution,
|
|
67
|
+
TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT,
|
|
68
|
+
VariationGenerationRequest,
|
|
69
|
+
VariationGenerationResult,
|
|
70
|
+
validate_reflection_evidence_catalog_result,
|
|
71
|
+
)
|
|
72
|
+
from agent_evolve.ports.structured_generator import (
|
|
73
|
+
MAX_PROMPT_UTF8_BYTES,
|
|
74
|
+
StructuredGenerationRequest,
|
|
75
|
+
StructuredGenerationResponse,
|
|
76
|
+
StructuredPromptLineage,
|
|
77
|
+
identity_prompt_lineage,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
CANDIDATE_PROPOSAL_TOOL_NAME = "return_candidate_proposal"
|
|
82
|
+
ATOMIC_MUTATION_TOOL_NAME = "return_atomic_mutation"
|
|
83
|
+
FINITE_VARIATION_SELECTION_TOOL_NAME = "select_finite_variation_option"
|
|
84
|
+
EXACT_PARENT_CROSSOVER_TOOL_NAME = "select_parent_crossover_loci"
|
|
85
|
+
REFLECTION_TOOL_NAME = "return_reflection_insights"
|
|
86
|
+
|
|
87
|
+
_CONFIGURATION_ROOT_DESCRIPTION = (
|
|
88
|
+
"The complete proposed candidate configuration. This field's value, rather "
|
|
89
|
+
"than the enclosing proposal object, is JSON-path root '$' for "
|
|
90
|
+
"intended_changes and source_attribution."
|
|
91
|
+
)
|
|
92
|
+
_INTENDED_CHANGES_DESCRIPTION = (
|
|
93
|
+
"Candidate-component JSON paths describing the intended edits. "
|
|
94
|
+
+ CANDIDATE_COMPONENT_PATH_CONTRACT
|
|
95
|
+
)
|
|
96
|
+
_SOURCE_ATTRIBUTION_DESCRIPTION = (
|
|
97
|
+
"Source claims at candidate-component JSON paths. "
|
|
98
|
+
+ CANDIDATE_COMPONENT_PATH_CONTRACT
|
|
99
|
+
)
|
|
100
|
+
_TWO_PARENT_INTENDED_CHANGES_DESCRIPTION = (
|
|
101
|
+
_INTENDED_CHANGES_DESCRIPTION + " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
|
|
102
|
+
)
|
|
103
|
+
_TWO_PARENT_SOURCE_ATTRIBUTION_DESCRIPTION = (
|
|
104
|
+
_SOURCE_ATTRIBUTION_DESCRIPTION + " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# These are local admission limits, not experiment policy. The provider sees a
|
|
108
|
+
# deliberately compact schema, while Pydantic retains these constraints in its
|
|
109
|
+
# core schema and validates them before a queue-owned attempt can succeed.
|
|
110
|
+
MAX_RATIONALE_CHARS = 16_384
|
|
111
|
+
MAX_CHANGE_CHARS = 4_096
|
|
112
|
+
MAX_INTENDED_CHANGES = 256
|
|
113
|
+
MAX_PATH_CHARS = 4_096
|
|
114
|
+
MAX_SOURCE_ATTRIBUTIONS = 4_096
|
|
115
|
+
MAX_ID_CHARS = 512
|
|
116
|
+
MAX_CLAIMED_INSIGHT_IDS = 1_024
|
|
117
|
+
MAX_PRESERVATION_IDS = 4_096
|
|
118
|
+
MAX_RELATION_ID_CHARS = 512
|
|
119
|
+
MAX_RESOLUTION_EXPLANATION_CHARS = 8_192
|
|
120
|
+
MAX_CONFLICT_RESOLUTIONS = 4_096
|
|
121
|
+
MAX_REFLECTION_TEXT_CHARS = 16_384
|
|
122
|
+
MAX_AFFECTED_PATHS = 256
|
|
123
|
+
MAX_EVIDENCE_CONTRAST_IDS = 256
|
|
124
|
+
|
|
125
|
+
_JSON_PATH_PATTERN = r"^\$([.\[].*)?$"
|
|
126
|
+
REFLECTION_WIRE_CONTRACT_REVISION = (
|
|
127
|
+
"reflection_wire_jsonpath_contract_v3_provider_grammar"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Kept below the limit declarations so the rendered note and provider schema
|
|
131
|
+
# derive from the same generic admission constants as local validation.
|
|
132
|
+
REFLECTION_OUTPUT_CONTRACT_NOTE = (
|
|
133
|
+
"REFLECTION OUTPUT CONTRACT\n"
|
|
134
|
+
f"Revision: {REFLECTION_WIRE_CONTRACT_REVISION}. "
|
|
135
|
+
f"For every insight, affected_paths must contain 1 to {MAX_AFFECTED_PATHS} "
|
|
136
|
+
"JSON-style paths. Every path must be rooted at '$' "
|
|
137
|
+
"(examples: '$', '$.field', or '$[0]')."
|
|
138
|
+
)
|
|
139
|
+
REFLECTION_OUTPUT_CONTRACT_NOTE_SHA256 = hashlib.sha256(
|
|
140
|
+
REFLECTION_OUTPUT_CONTRACT_NOTE.encode("utf-8", errors="strict")
|
|
141
|
+
).hexdigest()
|
|
142
|
+
REFLECTION_PROMPT_RENDERER_ID = "agent_evolve.reflection_prompt"
|
|
143
|
+
REFLECTION_PROMPT_RENDERER_REVISION = REFLECTION_WIRE_CONTRACT_REVISION
|
|
144
|
+
REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256 = hashlib.sha256(
|
|
145
|
+
(
|
|
146
|
+
"agent-evolve:reflection-prompt-renderer:v1\x00"
|
|
147
|
+
"algorithm=rstrip_then_two_newlines_then_contract_note_else_identity_at_"
|
|
148
|
+
"utf8_cap\x00"
|
|
149
|
+
f"wire_contract_revision={REFLECTION_WIRE_CONTRACT_REVISION}\x00"
|
|
150
|
+
f"contract_note_sha256={REFLECTION_OUTPUT_CONTRACT_NOTE_SHA256}\x00"
|
|
151
|
+
f"max_prompt_utf8_bytes={MAX_PROMPT_UTF8_BYTES}"
|
|
152
|
+
).encode("utf-8", errors="strict")
|
|
153
|
+
).hexdigest()
|
|
154
|
+
REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION = (
|
|
155
|
+
"reflection_wire_jsonpath_contract_v4_evidence_catalog"
|
|
156
|
+
)
|
|
157
|
+
REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256 = hashlib.sha256(
|
|
158
|
+
(
|
|
159
|
+
"agent-evolve:reflection-evidence-catalog-prompt-renderer:v1\x00"
|
|
160
|
+
"algorithm=rstrip_then_contract_note_then_canonical_catalog_or_fail_at_"
|
|
161
|
+
"utf8_cap\x00"
|
|
162
|
+
f"upstream_definition_sha256={REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256}"
|
|
163
|
+
"\x00citation_field=evidence_citation_keys\x00"
|
|
164
|
+
"citation_key_grammar=eNNNN\x00"
|
|
165
|
+
f"wire_contract_revision={REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION}"
|
|
166
|
+
"\x00"
|
|
167
|
+
f"max_prompt_utf8_bytes={MAX_PROMPT_UTF8_BYTES}"
|
|
168
|
+
).encode("utf-8", errors="strict")
|
|
169
|
+
).hexdigest()
|
|
170
|
+
REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION = "reflection_wire_semantic_contract_v5"
|
|
171
|
+
REFLECTION_SEMANTIC_PROMPT_RENDERER_DEFINITION_SHA256 = hashlib.sha256(
|
|
172
|
+
(
|
|
173
|
+
"agent-evolve:reflection-semantic-prompt-renderer:v1\x00"
|
|
174
|
+
"algorithm=legacy_note_then_semantic_contract_then_optional_catalog_or_fail_"
|
|
175
|
+
"at_utf8_cap\x00"
|
|
176
|
+
f"legacy_definition_sha256={REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256}"
|
|
177
|
+
"\x00"
|
|
178
|
+
"catalog_definition_sha256="
|
|
179
|
+
f"{REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256}\x00"
|
|
180
|
+
f"wire_contract_revision={REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION}\x00"
|
|
181
|
+
f"max_prompt_utf8_bytes={MAX_PROMPT_UTF8_BYTES}"
|
|
182
|
+
).encode("utf-8", errors="strict")
|
|
183
|
+
).hexdigest()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def render_reflection_prompt(
|
|
187
|
+
prompt: str,
|
|
188
|
+
evidence_catalog: ReflectionEvidenceCatalog | None = None,
|
|
189
|
+
*,
|
|
190
|
+
insight_contract: ReflectionInsightContract | None = None,
|
|
191
|
+
) -> str:
|
|
192
|
+
"""Render the exact provider prompt without exceeding the shared byte cap.
|
|
193
|
+
|
|
194
|
+
The structured schema always carries the rooted-path constraints. The
|
|
195
|
+
redundant natural-language note is appended only when the resulting prompt
|
|
196
|
+
remains valid for :class:`StructuredGenerationRequest`; otherwise the
|
|
197
|
+
original, already-valid prompt is preserved byte for byte.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
if type(prompt) is not str:
|
|
201
|
+
raise TypeError("prompt must be an exact string")
|
|
202
|
+
if not prompt.strip():
|
|
203
|
+
raise ValueError("prompt must be non-empty")
|
|
204
|
+
prompt_bytes = prompt.encode("utf-8", errors="strict")
|
|
205
|
+
if len(prompt_bytes) > MAX_PROMPT_UTF8_BYTES:
|
|
206
|
+
raise ValueError("prompt exceeds MAX_PROMPT_UTF8_BYTES")
|
|
207
|
+
semantic_note: str | None = None
|
|
208
|
+
if insight_contract is not None:
|
|
209
|
+
if type(insight_contract) is not ReflectionInsightContract:
|
|
210
|
+
raise TypeError(
|
|
211
|
+
"insight_contract must be an exact ReflectionInsightContract or None"
|
|
212
|
+
)
|
|
213
|
+
ReflectionInsightContract.__post_init__(insight_contract)
|
|
214
|
+
if insight_contract.is_semantic_v3:
|
|
215
|
+
semantic_projection = {
|
|
216
|
+
"allowed_comparison_anchor_kinds": [
|
|
217
|
+
value.value
|
|
218
|
+
for value in insight_contract.allowed_comparison_anchor_kinds
|
|
219
|
+
],
|
|
220
|
+
"allowed_consumer_scopes": [
|
|
221
|
+
value.value for value in insight_contract.allowed_consumer_scopes
|
|
222
|
+
],
|
|
223
|
+
"allowed_decision_paths": list(insight_contract.allowed_decision_paths),
|
|
224
|
+
"allowed_factor_capabilities": list(
|
|
225
|
+
insight_contract.allowed_factor_capabilities
|
|
226
|
+
),
|
|
227
|
+
"allowed_insight_kinds": [
|
|
228
|
+
value.value for value in insight_contract.allowed_insight_kinds
|
|
229
|
+
],
|
|
230
|
+
"allowed_source_role_ids": list(
|
|
231
|
+
insight_contract.allowed_source_role_ids
|
|
232
|
+
),
|
|
233
|
+
"required_metric_ids": list(insight_contract.required_metric_ids),
|
|
234
|
+
}
|
|
235
|
+
semantic_json = json.dumps(
|
|
236
|
+
semantic_projection,
|
|
237
|
+
ensure_ascii=True,
|
|
238
|
+
allow_nan=False,
|
|
239
|
+
separators=(",", ":"),
|
|
240
|
+
sort_keys=True,
|
|
241
|
+
)
|
|
242
|
+
semantic_note = (
|
|
243
|
+
"REFLECTION SEMANTIC CONTRACT\n"
|
|
244
|
+
f"Revision: {REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION}. "
|
|
245
|
+
"affected_paths contains only candidate decision paths from the "
|
|
246
|
+
"allowlist; never put a metric ID or factor capability there. "
|
|
247
|
+
"Put metric IDs only in effect_predictions and factor capability "
|
|
248
|
+
"IDs only in factor_capabilities. Every metric prediction requires "
|
|
249
|
+
"an explicit comparison_anchor and an adjudicable direction; never "
|
|
250
|
+
"emit unknown. Emit only exact closed-vocabulary values from this "
|
|
251
|
+
"request-scoped contract.\n"
|
|
252
|
+
f"Contract: {semantic_json}"
|
|
253
|
+
)
|
|
254
|
+
notes = [REFLECTION_OUTPUT_CONTRACT_NOTE]
|
|
255
|
+
if semantic_note is not None:
|
|
256
|
+
notes.append(semantic_note)
|
|
257
|
+
if evidence_catalog is not None:
|
|
258
|
+
if type(evidence_catalog) is not ReflectionEvidenceCatalog:
|
|
259
|
+
raise TypeError(
|
|
260
|
+
"evidence_catalog must be an exact ReflectionEvidenceCatalog or None"
|
|
261
|
+
)
|
|
262
|
+
ReflectionEvidenceCatalog.__post_init__(evidence_catalog)
|
|
263
|
+
catalog_json = json.dumps(
|
|
264
|
+
evidence_catalog.to_record(),
|
|
265
|
+
ensure_ascii=True,
|
|
266
|
+
allow_nan=False,
|
|
267
|
+
separators=(",", ":"),
|
|
268
|
+
sort_keys=True,
|
|
269
|
+
)
|
|
270
|
+
catalog_note = (
|
|
271
|
+
"REFLECTION EVIDENCE CITATION CATALOG\n"
|
|
272
|
+
f"Revision: {REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION}. "
|
|
273
|
+
"For evidence_citation_keys, emit only exact citation_key values "
|
|
274
|
+
"from this request-scoped catalog. Never emit a contrast_id, prefix, "
|
|
275
|
+
"or invented key in that field. The adapter resolves each exact key "
|
|
276
|
+
"to its authenticated full contrast_id.\n"
|
|
277
|
+
f"Catalog: {catalog_json}"
|
|
278
|
+
)
|
|
279
|
+
notes.append(catalog_note)
|
|
280
|
+
catalog_rendered = f"{prompt.rstrip()}\n\n" + "\n\n".join(notes)
|
|
281
|
+
if (
|
|
282
|
+
len(catalog_rendered.encode("utf-8", errors="strict"))
|
|
283
|
+
> MAX_PROMPT_UTF8_BYTES
|
|
284
|
+
):
|
|
285
|
+
raise ValueError(
|
|
286
|
+
"reflection evidence catalog cannot fit in the provider prompt"
|
|
287
|
+
)
|
|
288
|
+
return catalog_rendered
|
|
289
|
+
rendered = f"{prompt.rstrip()}\n\n" + "\n\n".join(notes)
|
|
290
|
+
if len(rendered.encode("utf-8", errors="strict")) <= MAX_PROMPT_UTF8_BYTES:
|
|
291
|
+
return rendered
|
|
292
|
+
if semantic_note is not None:
|
|
293
|
+
raise ValueError("reflection semantic contract cannot fit in provider prompt")
|
|
294
|
+
return prompt
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
_Rationale = Annotated[
|
|
298
|
+
str,
|
|
299
|
+
StringConstraints(
|
|
300
|
+
strict=True,
|
|
301
|
+
strip_whitespace=True,
|
|
302
|
+
min_length=1,
|
|
303
|
+
max_length=MAX_RATIONALE_CHARS,
|
|
304
|
+
),
|
|
305
|
+
]
|
|
306
|
+
_Change = Annotated[
|
|
307
|
+
str,
|
|
308
|
+
StringConstraints(
|
|
309
|
+
strict=True,
|
|
310
|
+
strip_whitespace=True,
|
|
311
|
+
min_length=1,
|
|
312
|
+
max_length=MAX_CHANGE_CHARS,
|
|
313
|
+
),
|
|
314
|
+
]
|
|
315
|
+
_Path = Annotated[
|
|
316
|
+
str,
|
|
317
|
+
StringConstraints(
|
|
318
|
+
strict=True,
|
|
319
|
+
strip_whitespace=True,
|
|
320
|
+
min_length=1,
|
|
321
|
+
max_length=MAX_PATH_CHARS,
|
|
322
|
+
pattern=_JSON_PATH_PATTERN,
|
|
323
|
+
),
|
|
324
|
+
]
|
|
325
|
+
_Identifier = Annotated[
|
|
326
|
+
str,
|
|
327
|
+
StringConstraints(
|
|
328
|
+
strict=True,
|
|
329
|
+
strip_whitespace=True,
|
|
330
|
+
min_length=1,
|
|
331
|
+
max_length=MAX_ID_CHARS,
|
|
332
|
+
),
|
|
333
|
+
]
|
|
334
|
+
_RelationIdentifier = Annotated[
|
|
335
|
+
str,
|
|
336
|
+
StringConstraints(
|
|
337
|
+
strict=True,
|
|
338
|
+
strip_whitespace=True,
|
|
339
|
+
min_length=1,
|
|
340
|
+
max_length=MAX_RELATION_ID_CHARS,
|
|
341
|
+
),
|
|
342
|
+
]
|
|
343
|
+
_ResolutionExplanation = Annotated[
|
|
344
|
+
str,
|
|
345
|
+
StringConstraints(
|
|
346
|
+
strict=True,
|
|
347
|
+
strip_whitespace=True,
|
|
348
|
+
min_length=1,
|
|
349
|
+
max_length=MAX_RESOLUTION_EXPLANATION_CHARS,
|
|
350
|
+
),
|
|
351
|
+
]
|
|
352
|
+
_ReflectionText = Annotated[
|
|
353
|
+
str,
|
|
354
|
+
StringConstraints(
|
|
355
|
+
strict=True,
|
|
356
|
+
strip_whitespace=True,
|
|
357
|
+
min_length=1,
|
|
358
|
+
max_length=MAX_REFLECTION_TEXT_CHARS,
|
|
359
|
+
),
|
|
360
|
+
]
|
|
361
|
+
_ContrastIdentifier = Annotated[
|
|
362
|
+
str,
|
|
363
|
+
StringConstraints(
|
|
364
|
+
strict=True,
|
|
365
|
+
pattern=r"^[0-9a-f]{64}$",
|
|
366
|
+
),
|
|
367
|
+
]
|
|
368
|
+
_EvidenceCitationKey = Annotated[
|
|
369
|
+
str,
|
|
370
|
+
StringConstraints(
|
|
371
|
+
strict=True,
|
|
372
|
+
pattern=r"^e[0-9]{4}$",
|
|
373
|
+
),
|
|
374
|
+
]
|
|
375
|
+
|
|
376
|
+
_STRICT_MODEL_CONFIG = ConfigDict(
|
|
377
|
+
extra="forbid",
|
|
378
|
+
strict=True,
|
|
379
|
+
frozen=True,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _path_text(path: JsonPath) -> str:
|
|
384
|
+
parts = ["$"]
|
|
385
|
+
for segment in path.segments:
|
|
386
|
+
if type(segment) is ObjectKey:
|
|
387
|
+
parts.append(f".{segment.value}")
|
|
388
|
+
elif type(segment) is ArrayIndex:
|
|
389
|
+
parts.append(f"[{segment.value}]")
|
|
390
|
+
else: # pragma: no cover - JsonPath closes the segment union.
|
|
391
|
+
raise AssertionError("unsupported path segment")
|
|
392
|
+
return "".join(parts)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _local_ref_value(root: dict[str, Any], reference: str) -> dict[str, Any]:
|
|
396
|
+
"""Resolve one local JSON pointer without accepting external schemas."""
|
|
397
|
+
|
|
398
|
+
if type(reference) is not str or not reference.startswith("#/"):
|
|
399
|
+
raise ValueError("atomic leaf schema contains a non-local $ref")
|
|
400
|
+
current: object = root
|
|
401
|
+
for raw_token in reference[2:].split("/"):
|
|
402
|
+
token = raw_token.replace("~1", "/").replace("~0", "~")
|
|
403
|
+
if type(current) is not dict or token not in current:
|
|
404
|
+
raise ValueError("atomic leaf schema contains an unresolved $ref")
|
|
405
|
+
current = current[token]
|
|
406
|
+
if type(current) is not dict:
|
|
407
|
+
raise ValueError("atomic leaf $ref must resolve to an object schema")
|
|
408
|
+
return current
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _dereference_schema_top(
|
|
412
|
+
schema: dict[str, Any],
|
|
413
|
+
root: dict[str, Any],
|
|
414
|
+
*,
|
|
415
|
+
references: tuple[str, ...] = (),
|
|
416
|
+
) -> dict[str, Any]:
|
|
417
|
+
"""Expand a top-level local ref while detecting recursive definitions."""
|
|
418
|
+
|
|
419
|
+
if "$ref" not in schema:
|
|
420
|
+
return copy.deepcopy(schema)
|
|
421
|
+
reference = schema["$ref"]
|
|
422
|
+
if type(reference) is not str:
|
|
423
|
+
raise ValueError("atomic leaf schema $ref must be a string")
|
|
424
|
+
if reference in references:
|
|
425
|
+
raise ValueError("atomic leaf schema contains a recursive $ref")
|
|
426
|
+
resolved = _dereference_schema_top(
|
|
427
|
+
_local_ref_value(root, reference),
|
|
428
|
+
root,
|
|
429
|
+
references=references + (reference,),
|
|
430
|
+
)
|
|
431
|
+
siblings = {key: value for key, value in schema.items() if key != "$ref"}
|
|
432
|
+
# Pydantic normally emits a bare local ref. Siblings are legal JSON Schema,
|
|
433
|
+
# but conflicting keys cannot be combined faithfully and therefore fail
|
|
434
|
+
# closed instead of weakening either constraint.
|
|
435
|
+
for key, value in siblings.items():
|
|
436
|
+
if key in resolved and resolved[key] != value:
|
|
437
|
+
raise ValueError("atomic leaf schema has conflicting $ref siblings")
|
|
438
|
+
resolved[key] = copy.deepcopy(value)
|
|
439
|
+
return resolved
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _descend_schema(
|
|
443
|
+
schema: dict[str, Any],
|
|
444
|
+
segment: ObjectKey | ArrayIndex,
|
|
445
|
+
root: dict[str, Any],
|
|
446
|
+
) -> dict[str, Any]:
|
|
447
|
+
current = _dereference_schema_top(schema, root)
|
|
448
|
+
if type(segment) is ObjectKey:
|
|
449
|
+
properties = current.get("properties")
|
|
450
|
+
if type(properties) is dict and segment.value in properties:
|
|
451
|
+
child = properties[segment.value]
|
|
452
|
+
if type(child) is not dict:
|
|
453
|
+
raise ValueError("atomic object property has no object schema")
|
|
454
|
+
return child
|
|
455
|
+
else:
|
|
456
|
+
items = current.get("items")
|
|
457
|
+
if type(items) is dict:
|
|
458
|
+
return items
|
|
459
|
+
prefix_items = current.get("prefixItems")
|
|
460
|
+
if type(prefix_items) is list and segment.value < len(prefix_items):
|
|
461
|
+
child = prefix_items[segment.value]
|
|
462
|
+
if type(child) is not dict:
|
|
463
|
+
raise ValueError("atomic tuple item has no object schema")
|
|
464
|
+
return child
|
|
465
|
+
|
|
466
|
+
for composition_key in ("anyOf", "oneOf", "allOf"):
|
|
467
|
+
variants = current.get(composition_key)
|
|
468
|
+
if type(variants) is not list:
|
|
469
|
+
continue
|
|
470
|
+
descended: list[dict[str, Any]] = []
|
|
471
|
+
for variant in variants:
|
|
472
|
+
if type(variant) is not dict:
|
|
473
|
+
raise ValueError("atomic schema composition is malformed")
|
|
474
|
+
try:
|
|
475
|
+
descended.append(_descend_schema(variant, segment, root))
|
|
476
|
+
except ValueError:
|
|
477
|
+
continue
|
|
478
|
+
if not descended:
|
|
479
|
+
continue
|
|
480
|
+
if len(descended) == 1:
|
|
481
|
+
return descended[0]
|
|
482
|
+
return {composition_key: descended}
|
|
483
|
+
raise ValueError("atomic editable path cannot be resolved in candidate schema")
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def _expand_local_refs(
|
|
487
|
+
value: object,
|
|
488
|
+
root: dict[str, Any],
|
|
489
|
+
*,
|
|
490
|
+
references: tuple[str, ...] = (),
|
|
491
|
+
) -> object:
|
|
492
|
+
if type(value) is list:
|
|
493
|
+
return [_expand_local_refs(item, root, references=references) for item in value]
|
|
494
|
+
if type(value) is not dict:
|
|
495
|
+
return copy.deepcopy(value)
|
|
496
|
+
current = value
|
|
497
|
+
if "$ref" in current:
|
|
498
|
+
reference = current["$ref"]
|
|
499
|
+
if type(reference) is not str:
|
|
500
|
+
raise ValueError("atomic leaf schema $ref must be a string")
|
|
501
|
+
if reference in references:
|
|
502
|
+
raise ValueError("atomic leaf schema contains a recursive $ref")
|
|
503
|
+
current = _dereference_schema_top(
|
|
504
|
+
current,
|
|
505
|
+
root,
|
|
506
|
+
references=references,
|
|
507
|
+
)
|
|
508
|
+
references = references + (reference,)
|
|
509
|
+
return {
|
|
510
|
+
key: _expand_local_refs(item, root, references=references)
|
|
511
|
+
for key, item in current.items()
|
|
512
|
+
if key != "$defs"
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _schema_is_scalar(schema: dict[str, Any]) -> bool:
|
|
517
|
+
scalar_types = {"null", "boolean", "integer", "number", "string"}
|
|
518
|
+
if "const" in schema and not is_json_scalar(schema["const"]):
|
|
519
|
+
return False
|
|
520
|
+
enum = schema.get("enum")
|
|
521
|
+
if type(enum) is list and (
|
|
522
|
+
not enum or not all(is_json_scalar(item) for item in enum)
|
|
523
|
+
):
|
|
524
|
+
return False
|
|
525
|
+
declared_type = schema.get("type")
|
|
526
|
+
if type(declared_type) is str and declared_type in scalar_types:
|
|
527
|
+
return True
|
|
528
|
+
if (
|
|
529
|
+
type(declared_type) is list
|
|
530
|
+
and declared_type
|
|
531
|
+
and all(type(item) is str and item in scalar_types for item in declared_type)
|
|
532
|
+
):
|
|
533
|
+
return True
|
|
534
|
+
if "const" in schema:
|
|
535
|
+
return True
|
|
536
|
+
if type(enum) is list:
|
|
537
|
+
return True
|
|
538
|
+
for key in ("anyOf", "oneOf", "allOf"):
|
|
539
|
+
variants = schema.get(key)
|
|
540
|
+
if (
|
|
541
|
+
type(variants) is list
|
|
542
|
+
and variants
|
|
543
|
+
and all(type(item) is dict and _schema_is_scalar(item) for item in variants)
|
|
544
|
+
):
|
|
545
|
+
return True
|
|
546
|
+
return False
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def _atomic_leaf_schema(
|
|
550
|
+
candidate_model: type[BaseModel],
|
|
551
|
+
path: JsonPath,
|
|
552
|
+
) -> dict[str, Any]:
|
|
553
|
+
"""Resolve one exact candidate-model leaf into a closed scalar schema."""
|
|
554
|
+
|
|
555
|
+
root = candidate_model.model_json_schema(by_alias=False)
|
|
556
|
+
if type(root) is not dict:
|
|
557
|
+
raise TypeError("candidate_model must return an object JSON schema")
|
|
558
|
+
current = root
|
|
559
|
+
for segment in path.segments:
|
|
560
|
+
current = _descend_schema(current, segment, root)
|
|
561
|
+
expanded = _expand_local_refs(current, root)
|
|
562
|
+
if type(expanded) is not dict or not _schema_is_scalar(expanded):
|
|
563
|
+
raise ValueError(
|
|
564
|
+
"atomic editable path must resolve to a closed scalar JSON schema"
|
|
565
|
+
)
|
|
566
|
+
return expanded
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _exclude_current_scalar(
|
|
570
|
+
schema: dict[str, Any],
|
|
571
|
+
current: object,
|
|
572
|
+
) -> dict[str, Any]:
|
|
573
|
+
"""Make a provider-visible scalar schema exclude the observed parent value."""
|
|
574
|
+
|
|
575
|
+
frozen_current = freeze_json(current)
|
|
576
|
+
if not is_json_scalar(frozen_current):
|
|
577
|
+
raise ValueError("atomic parent leaf must be a typed-JSON scalar")
|
|
578
|
+
result = copy.deepcopy(schema)
|
|
579
|
+
enum = result.get("enum")
|
|
580
|
+
if type(enum) is list:
|
|
581
|
+
remaining = [
|
|
582
|
+
item
|
|
583
|
+
for item in enum
|
|
584
|
+
if not typed_json_equal(freeze_json(item), frozen_current)
|
|
585
|
+
]
|
|
586
|
+
if len(remaining) != len(enum):
|
|
587
|
+
if not remaining:
|
|
588
|
+
raise ValueError("atomic leaf schema permits only the current value")
|
|
589
|
+
result["enum"] = remaining
|
|
590
|
+
return result
|
|
591
|
+
if "const" in result and typed_json_equal(
|
|
592
|
+
freeze_json(result["const"]), frozen_current
|
|
593
|
+
):
|
|
594
|
+
raise ValueError("atomic leaf schema permits only the current value")
|
|
595
|
+
exclusion = {"not": {"const": thaw_json(frozen_current)}}
|
|
596
|
+
if "not" not in result:
|
|
597
|
+
result.update(exclusion)
|
|
598
|
+
return result
|
|
599
|
+
return {"allOf": [result, exclusion]}
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _restrict_scalar_options(
|
|
603
|
+
schema: dict[str, Any],
|
|
604
|
+
options: tuple[FrozenJsonValue, ...],
|
|
605
|
+
) -> dict[str, Any]:
|
|
606
|
+
"""Intersect a leaf schema with one exact provider-visible option order."""
|
|
607
|
+
|
|
608
|
+
if not options:
|
|
609
|
+
return copy.deepcopy(schema)
|
|
610
|
+
result = copy.deepcopy(schema)
|
|
611
|
+
result["enum"] = [thaw_json(option) for option in options]
|
|
612
|
+
return result
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def _compact_candidate_json_schema(model: type[BaseModel]) -> dict[str, Any]:
|
|
616
|
+
"""Return the small provider contract while preserving the model's core schema."""
|
|
617
|
+
|
|
618
|
+
properties: dict[str, object] = {}
|
|
619
|
+
for name in model.model_fields:
|
|
620
|
+
if name == "configuration":
|
|
621
|
+
field_schema: dict[str, object] = {
|
|
622
|
+
"type": "object",
|
|
623
|
+
"additionalProperties": True,
|
|
624
|
+
}
|
|
625
|
+
elif name == "design_rationale":
|
|
626
|
+
field_schema = {"type": "string"}
|
|
627
|
+
elif name == "intended_changes":
|
|
628
|
+
evidence_path_contract = CANDIDATE_COMPONENT_PATH_CONTRACT
|
|
629
|
+
field_description = model.model_fields[name].description
|
|
630
|
+
if (
|
|
631
|
+
field_description is not None
|
|
632
|
+
and TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT in field_description
|
|
633
|
+
):
|
|
634
|
+
evidence_path_contract += " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
|
|
635
|
+
field_schema = {
|
|
636
|
+
"type": "array",
|
|
637
|
+
"items": {
|
|
638
|
+
"type": "string",
|
|
639
|
+
"description": evidence_path_contract,
|
|
640
|
+
},
|
|
641
|
+
}
|
|
642
|
+
elif name in {
|
|
643
|
+
"claimed_insight_ids",
|
|
644
|
+
"claimed_preservation_obligation_ids",
|
|
645
|
+
}:
|
|
646
|
+
field_schema = {"type": "array", "items": {"type": "string"}}
|
|
647
|
+
elif name == "source_attribution":
|
|
648
|
+
evidence_path_contract = CANDIDATE_COMPONENT_PATH_CONTRACT
|
|
649
|
+
field_description = model.model_fields[name].description
|
|
650
|
+
if (
|
|
651
|
+
field_description is not None
|
|
652
|
+
and TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT in field_description
|
|
653
|
+
):
|
|
654
|
+
evidence_path_contract += " " + TWO_PARENT_CROSSOVER_EVIDENCE_CONTRACT
|
|
655
|
+
attribution_annotation = model.model_fields[name].annotation
|
|
656
|
+
attribution_args = get_args(attribution_annotation)
|
|
657
|
+
if (
|
|
658
|
+
len(attribution_args) != 1
|
|
659
|
+
or not isinstance(attribution_args[0], type)
|
|
660
|
+
or not issubclass(attribution_args[0], BaseModel)
|
|
661
|
+
):
|
|
662
|
+
raise RuntimeError("source attribution field lost its item model")
|
|
663
|
+
source_annotation = attribution_args[0].model_fields["source"].annotation
|
|
664
|
+
source_values = get_args(source_annotation)
|
|
665
|
+
if not source_values or any(
|
|
666
|
+
type(value) is not str for value in source_values
|
|
667
|
+
):
|
|
668
|
+
raise RuntimeError(
|
|
669
|
+
"source attribution field lost its closed vocabulary"
|
|
670
|
+
)
|
|
671
|
+
field_schema = {
|
|
672
|
+
"type": "array",
|
|
673
|
+
"items": {
|
|
674
|
+
"type": "object",
|
|
675
|
+
"properties": {
|
|
676
|
+
"path": {
|
|
677
|
+
"type": "string",
|
|
678
|
+
"description": evidence_path_contract,
|
|
679
|
+
},
|
|
680
|
+
"source": {
|
|
681
|
+
"type": "string",
|
|
682
|
+
"enum": list(source_values),
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
"required": ["path", "source"],
|
|
686
|
+
"additionalProperties": False,
|
|
687
|
+
},
|
|
688
|
+
}
|
|
689
|
+
elif name == "conflict_resolutions":
|
|
690
|
+
field_schema = {
|
|
691
|
+
"type": "array",
|
|
692
|
+
"items": {
|
|
693
|
+
"type": "object",
|
|
694
|
+
"properties": {
|
|
695
|
+
"relation_id": {"type": "string"},
|
|
696
|
+
"choice": {
|
|
697
|
+
"type": "string",
|
|
698
|
+
"enum": [
|
|
699
|
+
"choose_left",
|
|
700
|
+
"choose_right",
|
|
701
|
+
"synthesize",
|
|
702
|
+
"drop_both",
|
|
703
|
+
],
|
|
704
|
+
},
|
|
705
|
+
"explanation": {"type": "string"},
|
|
706
|
+
},
|
|
707
|
+
"required": ["relation_id", "choice", "explanation"],
|
|
708
|
+
"additionalProperties": False,
|
|
709
|
+
},
|
|
710
|
+
}
|
|
711
|
+
else: # pragma: no cover - guarded by the closed dynamic field builder.
|
|
712
|
+
raise RuntimeError(f"unsupported candidate wire field: {name}")
|
|
713
|
+
field_description = model.model_fields[name].description
|
|
714
|
+
if field_description is not None:
|
|
715
|
+
field_schema["description"] = field_description
|
|
716
|
+
properties[name] = field_schema
|
|
717
|
+
required = [
|
|
718
|
+
name for name, field in model.model_fields.items() if field.is_required()
|
|
719
|
+
]
|
|
720
|
+
schema: dict[str, Any] = {
|
|
721
|
+
"type": "object",
|
|
722
|
+
"properties": properties,
|
|
723
|
+
"additionalProperties": False,
|
|
724
|
+
}
|
|
725
|
+
if required:
|
|
726
|
+
schema["required"] = required
|
|
727
|
+
return schema
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
class _CompactCandidateSchemaBase(BaseModel):
|
|
731
|
+
"""Full local validation with a provider-compatible JSON-schema projection."""
|
|
732
|
+
|
|
733
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
734
|
+
|
|
735
|
+
@classmethod
|
|
736
|
+
def __get_pydantic_json_schema__(
|
|
737
|
+
cls,
|
|
738
|
+
_core_schema: Any,
|
|
739
|
+
_handler: Any,
|
|
740
|
+
) -> dict[str, Any]:
|
|
741
|
+
return _compact_candidate_json_schema(cls)
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
_AtomicScalar = bool | int | float | str | None
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
class _CompactAtomicMutationSchemaBase(BaseModel):
|
|
748
|
+
"""One scalar wire edit plus whole-candidate semantic validation."""
|
|
749
|
+
|
|
750
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
751
|
+
expected_path_text: ClassVar[str]
|
|
752
|
+
replacement_schema: ClassVar[dict[str, Any]]
|
|
753
|
+
candidate_model: ClassVar[type[BaseModel]]
|
|
754
|
+
output_contract: ClassVar[AtomicMutationOutputContract]
|
|
755
|
+
|
|
756
|
+
@classmethod
|
|
757
|
+
def __get_pydantic_json_schema__(
|
|
758
|
+
cls,
|
|
759
|
+
_core_schema: Any,
|
|
760
|
+
_handler: Any,
|
|
761
|
+
) -> dict[str, Any]:
|
|
762
|
+
return {
|
|
763
|
+
"type": "object",
|
|
764
|
+
"properties": {
|
|
765
|
+
"path": {
|
|
766
|
+
"type": "string",
|
|
767
|
+
"const": cls.expected_path_text,
|
|
768
|
+
},
|
|
769
|
+
"replacement": copy.deepcopy(cls.replacement_schema),
|
|
770
|
+
"design_rationale": {"type": "string"},
|
|
771
|
+
"claimed_insight_ids": {
|
|
772
|
+
"type": "array",
|
|
773
|
+
"items": {"type": "string"},
|
|
774
|
+
},
|
|
775
|
+
},
|
|
776
|
+
"required": ["path", "replacement", "design_rationale"],
|
|
777
|
+
"additionalProperties": False,
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
@model_validator(mode="after")
|
|
781
|
+
def _validate_atomic_candidate(self) -> "_CompactAtomicMutationSchemaBase":
|
|
782
|
+
contract = type(self).output_contract
|
|
783
|
+
if self.path != type(self).expected_path_text:
|
|
784
|
+
raise ValueError("atomic mutation returned the wrong path")
|
|
785
|
+
replacement = freeze_json(self.replacement)
|
|
786
|
+
if not is_json_scalar(replacement): # pragma: no cover - closed field union.
|
|
787
|
+
raise ValueError("atomic replacement must be a typed-JSON scalar")
|
|
788
|
+
old_value = value_at_path(
|
|
789
|
+
contract.parent_configuration,
|
|
790
|
+
contract.editable_path,
|
|
791
|
+
)
|
|
792
|
+
if typed_json_equal(old_value, replacement):
|
|
793
|
+
raise ValueError("atomic replacement must differ from the parent value")
|
|
794
|
+
if contract.replacement_options and not any(
|
|
795
|
+
typed_json_equal(replacement, option)
|
|
796
|
+
for option in contract.replacement_options
|
|
797
|
+
):
|
|
798
|
+
raise ValueError(
|
|
799
|
+
"atomic replacement is outside the contracted option catalog"
|
|
800
|
+
)
|
|
801
|
+
target = replace_existing_path(
|
|
802
|
+
contract.parent_configuration,
|
|
803
|
+
contract.editable_path,
|
|
804
|
+
replacement,
|
|
805
|
+
)
|
|
806
|
+
validated = type(self).candidate_model.model_validate(
|
|
807
|
+
thaw_json(target),
|
|
808
|
+
strict=True,
|
|
809
|
+
by_alias=False,
|
|
810
|
+
by_name=True,
|
|
811
|
+
)
|
|
812
|
+
validated_frozen = freeze_json(_configuration_dict(validated))
|
|
813
|
+
if not typed_json_equal(validated_frozen, target):
|
|
814
|
+
raise ValueError("candidate validation changed the typed atomic target")
|
|
815
|
+
return self
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
class _CompactExactParentCrossoverSchemaBase(BaseModel):
|
|
819
|
+
"""A proper donor-locus subset behind a tiny provider-visible schema."""
|
|
820
|
+
|
|
821
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
822
|
+
allowed_locus_ids: ClassVar[tuple[str, ...]] = ()
|
|
823
|
+
claimable_insight_ids: ClassVar[tuple[str, ...]] = ()
|
|
824
|
+
forbidden_import_locus_sets: ClassVar[tuple[tuple[str, ...], ...]] = ()
|
|
825
|
+
|
|
826
|
+
@classmethod
|
|
827
|
+
def __get_pydantic_json_schema__(
|
|
828
|
+
cls,
|
|
829
|
+
_core_schema: Any,
|
|
830
|
+
_handler: Any,
|
|
831
|
+
) -> dict[str, Any]:
|
|
832
|
+
locus_count = len(cls.allowed_locus_ids)
|
|
833
|
+
if locus_count < 2: # pragma: no cover - construction validates first.
|
|
834
|
+
raise RuntimeError("exact crossover wire lost its finite loci")
|
|
835
|
+
claimable_insight_ids = cls.claimable_insight_ids
|
|
836
|
+
claimed_items: dict[str, object] = {
|
|
837
|
+
"type": "string",
|
|
838
|
+
"minLength": 1,
|
|
839
|
+
"maxLength": MAX_ID_CHARS,
|
|
840
|
+
}
|
|
841
|
+
if claimable_insight_ids:
|
|
842
|
+
claimed_items["enum"] = list(claimable_insight_ids)
|
|
843
|
+
import_schema: dict[str, object] = {
|
|
844
|
+
"type": "array",
|
|
845
|
+
"description": (
|
|
846
|
+
"Import these exact donor-parent loci into the sealed "
|
|
847
|
+
"base parent. Omitted loci remain from the base parent. "
|
|
848
|
+
"Do not select any explicitly forbidden exact set."
|
|
849
|
+
),
|
|
850
|
+
"items": {
|
|
851
|
+
"type": "string",
|
|
852
|
+
"enum": list(cls.allowed_locus_ids),
|
|
853
|
+
},
|
|
854
|
+
"uniqueItems": True,
|
|
855
|
+
"minItems": 1,
|
|
856
|
+
"maxItems": locus_count - 1,
|
|
857
|
+
}
|
|
858
|
+
if cls.forbidden_import_locus_sets:
|
|
859
|
+
# Each clause excludes one set independent of array order. With
|
|
860
|
+
# uniqueItems, equal cardinality plus containment of every member
|
|
861
|
+
# is exact set equality; no enumeration of the remaining action
|
|
862
|
+
# space is needed.
|
|
863
|
+
import_schema["allOf"] = [
|
|
864
|
+
{
|
|
865
|
+
"not": {
|
|
866
|
+
"allOf": [
|
|
867
|
+
{"minItems": len(forbidden)},
|
|
868
|
+
{"maxItems": len(forbidden)},
|
|
869
|
+
*(
|
|
870
|
+
{"contains": {"const": locus_id}}
|
|
871
|
+
for locus_id in forbidden
|
|
872
|
+
),
|
|
873
|
+
]
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
for forbidden in cls.forbidden_import_locus_sets
|
|
877
|
+
]
|
|
878
|
+
return {
|
|
879
|
+
"type": "object",
|
|
880
|
+
"properties": {
|
|
881
|
+
"import_locus_ids": import_schema,
|
|
882
|
+
"claimed_insight_ids": {
|
|
883
|
+
"type": "array",
|
|
884
|
+
"items": claimed_items,
|
|
885
|
+
"uniqueItems": True,
|
|
886
|
+
"maxItems": len(claimable_insight_ids),
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
"required": ["import_locus_ids"],
|
|
890
|
+
"additionalProperties": False,
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
@model_validator(mode="after")
|
|
894
|
+
def _validate_exact_parent_subset(
|
|
895
|
+
self,
|
|
896
|
+
) -> "_CompactExactParentCrossoverSchemaBase":
|
|
897
|
+
allowed = type(self).allowed_locus_ids
|
|
898
|
+
imported = tuple(self.import_locus_ids)
|
|
899
|
+
if not imported or len(imported) >= len(allowed):
|
|
900
|
+
raise ValueError("import_locus_ids must be a proper nonempty donor subset")
|
|
901
|
+
if len(set(imported)) != len(imported):
|
|
902
|
+
raise ValueError("import_locus_ids cannot contain duplicates")
|
|
903
|
+
if not set(imported).issubset(allowed):
|
|
904
|
+
raise ValueError("import_locus_ids escaped the sealed locus catalog")
|
|
905
|
+
if tuple(sorted(imported)) in type(self).forbidden_import_locus_sets:
|
|
906
|
+
raise ValueError(
|
|
907
|
+
"import_locus_ids matches a forbidden known-child materialization"
|
|
908
|
+
)
|
|
909
|
+
if len(set(self.claimed_insight_ids)) != len(self.claimed_insight_ids):
|
|
910
|
+
raise ValueError("claimed_insight_ids cannot contain duplicates")
|
|
911
|
+
if not set(self.claimed_insight_ids).issubset(type(self).claimable_insight_ids):
|
|
912
|
+
raise ValueError("claimed_insight_ids escaped the assigned insight catalog")
|
|
913
|
+
return self
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
class _CompactReflectionSchemaBase(BaseModel):
|
|
917
|
+
"""Bounded local reflection validation behind one generic wire envelope."""
|
|
918
|
+
|
|
919
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
920
|
+
available_contrast_ids: ClassVar[tuple[str, ...]] = ()
|
|
921
|
+
available_evidence_citation_keys: ClassVar[tuple[str, ...]] = ()
|
|
922
|
+
evidence_catalog_identity_sha256: ClassVar[str | None] = None
|
|
923
|
+
insight_contract: ClassVar[ReflectionInsightContract | None] = None
|
|
924
|
+
min_insights: ClassVar[int] = 0
|
|
925
|
+
max_insights: ClassVar[int] = 4
|
|
926
|
+
|
|
927
|
+
@model_validator(mode="after")
|
|
928
|
+
def _validate_unique_normalized_claims(self) -> "_CompactReflectionSchemaBase":
|
|
929
|
+
insights = tuple(getattr(self, "insights", ()))
|
|
930
|
+
claims = tuple(
|
|
931
|
+
" ".join(value.claim.strip().casefold().split()) for value in insights
|
|
932
|
+
)
|
|
933
|
+
if len(set(claims)) != len(claims):
|
|
934
|
+
raise ValueError("reflection insights must have distinct normalized claims")
|
|
935
|
+
return self
|
|
936
|
+
|
|
937
|
+
@classmethod
|
|
938
|
+
def __get_pydantic_json_schema__(
|
|
939
|
+
cls,
|
|
940
|
+
_core_schema: Any,
|
|
941
|
+
_handler: Any,
|
|
942
|
+
) -> dict[str, Any]:
|
|
943
|
+
reflection_text_schema: dict[str, Any] = {
|
|
944
|
+
"type": "string",
|
|
945
|
+
"minLength": 1,
|
|
946
|
+
"maxLength": MAX_REFLECTION_TEXT_CHARS,
|
|
947
|
+
}
|
|
948
|
+
catalog_mode = bool(cls.available_evidence_citation_keys)
|
|
949
|
+
citation_field_name = (
|
|
950
|
+
"evidence_citation_keys" if catalog_mode else "evidence_contrast_ids"
|
|
951
|
+
)
|
|
952
|
+
citation_values = (
|
|
953
|
+
cls.available_evidence_citation_keys
|
|
954
|
+
if catalog_mode
|
|
955
|
+
else cls.available_contrast_ids
|
|
956
|
+
)
|
|
957
|
+
contrast_item_schema: dict[str, Any] = {
|
|
958
|
+
"type": "string",
|
|
959
|
+
"pattern": "^e[0-9]{4}$" if catalog_mode else "^[0-9a-f]{64}$",
|
|
960
|
+
}
|
|
961
|
+
if citation_values:
|
|
962
|
+
contrast_item_schema["enum"] = list(citation_values)
|
|
963
|
+
insight_properties: dict[str, Any] = {
|
|
964
|
+
"claim": copy.deepcopy(reflection_text_schema),
|
|
965
|
+
"trigger": copy.deepcopy(reflection_text_schema),
|
|
966
|
+
"mechanism": copy.deepcopy(reflection_text_schema),
|
|
967
|
+
"affected_paths": {
|
|
968
|
+
"type": "array",
|
|
969
|
+
"minItems": 1,
|
|
970
|
+
"maxItems": MAX_AFFECTED_PATHS,
|
|
971
|
+
"items": {
|
|
972
|
+
"type": "string",
|
|
973
|
+
"minLength": 1,
|
|
974
|
+
"maxLength": MAX_PATH_CHARS,
|
|
975
|
+
"pattern": _JSON_PATH_PATTERN,
|
|
976
|
+
"description": (
|
|
977
|
+
"A JSON-style path rooted at '$', such as '$.field' or '$[0]'."
|
|
978
|
+
),
|
|
979
|
+
},
|
|
980
|
+
},
|
|
981
|
+
"evidence_summary": copy.deepcopy(reflection_text_schema),
|
|
982
|
+
citation_field_name: {
|
|
983
|
+
"type": "array",
|
|
984
|
+
"items": contrast_item_schema,
|
|
985
|
+
"uniqueItems": True,
|
|
986
|
+
"minItems": (1 if citation_values else 0),
|
|
987
|
+
"maxItems": (
|
|
988
|
+
min(
|
|
989
|
+
MAX_EVIDENCE_CONTRAST_IDS,
|
|
990
|
+
len(citation_values),
|
|
991
|
+
)
|
|
992
|
+
if citation_values
|
|
993
|
+
else 0
|
|
994
|
+
),
|
|
995
|
+
},
|
|
996
|
+
"confidence": {
|
|
997
|
+
"type": "number",
|
|
998
|
+
"minimum": 0.0,
|
|
999
|
+
"maximum": 1.0,
|
|
1000
|
+
},
|
|
1001
|
+
}
|
|
1002
|
+
required = [
|
|
1003
|
+
"claim",
|
|
1004
|
+
"trigger",
|
|
1005
|
+
"mechanism",
|
|
1006
|
+
"affected_paths",
|
|
1007
|
+
"evidence_summary",
|
|
1008
|
+
citation_field_name,
|
|
1009
|
+
"confidence",
|
|
1010
|
+
]
|
|
1011
|
+
contract = cls.insight_contract
|
|
1012
|
+
if contract is not None:
|
|
1013
|
+
ReflectionInsightContract.__post_init__(contract)
|
|
1014
|
+
comparison_properties: dict[str, Any] = {}
|
|
1015
|
+
comparison_required: list[str] = []
|
|
1016
|
+
if contract.is_semantic_v3:
|
|
1017
|
+
role_schema: dict[str, Any] = {"type": "null"}
|
|
1018
|
+
if contract.allowed_source_role_ids:
|
|
1019
|
+
role_schema = {
|
|
1020
|
+
"anyOf": [
|
|
1021
|
+
{
|
|
1022
|
+
"type": "string",
|
|
1023
|
+
"enum": list(contract.allowed_source_role_ids),
|
|
1024
|
+
},
|
|
1025
|
+
{"type": "null"},
|
|
1026
|
+
]
|
|
1027
|
+
}
|
|
1028
|
+
comparison_properties["comparison_anchor"] = {
|
|
1029
|
+
"type": "object",
|
|
1030
|
+
"properties": {
|
|
1031
|
+
"kind": {
|
|
1032
|
+
"type": "string",
|
|
1033
|
+
"enum": [
|
|
1034
|
+
value.value
|
|
1035
|
+
for value in (contract.allowed_comparison_anchor_kinds)
|
|
1036
|
+
],
|
|
1037
|
+
},
|
|
1038
|
+
"source_role_id": role_schema,
|
|
1039
|
+
},
|
|
1040
|
+
"required": ["kind", "source_role_id"],
|
|
1041
|
+
"additionalProperties": False,
|
|
1042
|
+
}
|
|
1043
|
+
comparison_required.append("comparison_anchor")
|
|
1044
|
+
insight_properties.update(
|
|
1045
|
+
{
|
|
1046
|
+
"effect_predictions": {
|
|
1047
|
+
"type": "array",
|
|
1048
|
+
"items": {
|
|
1049
|
+
"type": "object",
|
|
1050
|
+
"properties": {
|
|
1051
|
+
"metric_id": {
|
|
1052
|
+
"type": "string",
|
|
1053
|
+
"enum": list(contract.required_metric_ids),
|
|
1054
|
+
},
|
|
1055
|
+
"direction": {
|
|
1056
|
+
"type": "string",
|
|
1057
|
+
"enum": [
|
|
1058
|
+
direction.value
|
|
1059
|
+
for direction in MetricEffectDirection
|
|
1060
|
+
],
|
|
1061
|
+
},
|
|
1062
|
+
**comparison_properties,
|
|
1063
|
+
},
|
|
1064
|
+
"required": [
|
|
1065
|
+
"metric_id",
|
|
1066
|
+
"direction",
|
|
1067
|
+
*comparison_required,
|
|
1068
|
+
],
|
|
1069
|
+
"additionalProperties": False,
|
|
1070
|
+
},
|
|
1071
|
+
"uniqueItems": True,
|
|
1072
|
+
"minItems": len(contract.required_metric_ids),
|
|
1073
|
+
"maxItems": len(contract.required_metric_ids),
|
|
1074
|
+
},
|
|
1075
|
+
"recommended_option_families": {
|
|
1076
|
+
"type": "array",
|
|
1077
|
+
"items": {
|
|
1078
|
+
"type": "string",
|
|
1079
|
+
"enum": list(contract.allowed_option_families),
|
|
1080
|
+
},
|
|
1081
|
+
"uniqueItems": True,
|
|
1082
|
+
"minItems": 1,
|
|
1083
|
+
"maxItems": len(contract.allowed_option_families),
|
|
1084
|
+
},
|
|
1085
|
+
"action_template": copy.deepcopy(reflection_text_schema),
|
|
1086
|
+
"falsification_condition": copy.deepcopy(reflection_text_schema),
|
|
1087
|
+
}
|
|
1088
|
+
)
|
|
1089
|
+
if contract.is_semantic_v3:
|
|
1090
|
+
insight_properties["affected_paths"].update(
|
|
1091
|
+
{
|
|
1092
|
+
"uniqueItems": True,
|
|
1093
|
+
"maxItems": min(
|
|
1094
|
+
MAX_AFFECTED_PATHS,
|
|
1095
|
+
len(contract.allowed_decision_paths),
|
|
1096
|
+
),
|
|
1097
|
+
}
|
|
1098
|
+
)
|
|
1099
|
+
insight_properties["affected_paths"]["items"]["enum"] = list(
|
|
1100
|
+
contract.allowed_decision_paths
|
|
1101
|
+
)
|
|
1102
|
+
insight_properties.update(
|
|
1103
|
+
{
|
|
1104
|
+
"insight_kind": {
|
|
1105
|
+
"type": "string",
|
|
1106
|
+
"enum": [
|
|
1107
|
+
value.value for value in contract.allowed_insight_kinds
|
|
1108
|
+
],
|
|
1109
|
+
},
|
|
1110
|
+
"consumer_scopes": {
|
|
1111
|
+
"type": "array",
|
|
1112
|
+
"items": {
|
|
1113
|
+
"type": "string",
|
|
1114
|
+
"enum": [
|
|
1115
|
+
value.value
|
|
1116
|
+
for value in contract.allowed_consumer_scopes
|
|
1117
|
+
],
|
|
1118
|
+
},
|
|
1119
|
+
"uniqueItems": True,
|
|
1120
|
+
"minItems": 1,
|
|
1121
|
+
"maxItems": len(contract.allowed_consumer_scopes),
|
|
1122
|
+
},
|
|
1123
|
+
"factor_capabilities": {
|
|
1124
|
+
"type": "array",
|
|
1125
|
+
"items": (
|
|
1126
|
+
{
|
|
1127
|
+
"type": "string",
|
|
1128
|
+
"enum": list(contract.allowed_factor_capabilities),
|
|
1129
|
+
}
|
|
1130
|
+
if contract.allowed_factor_capabilities
|
|
1131
|
+
else {"type": "string"}
|
|
1132
|
+
),
|
|
1133
|
+
"uniqueItems": True,
|
|
1134
|
+
"minItems": 0,
|
|
1135
|
+
"maxItems": len(contract.allowed_factor_capabilities),
|
|
1136
|
+
},
|
|
1137
|
+
}
|
|
1138
|
+
)
|
|
1139
|
+
if contract.allowed_option_ids:
|
|
1140
|
+
insight_properties["recommended_option_ids"] = {
|
|
1141
|
+
"type": "array",
|
|
1142
|
+
"items": {
|
|
1143
|
+
"type": "string",
|
|
1144
|
+
"enum": list(contract.allowed_option_ids),
|
|
1145
|
+
},
|
|
1146
|
+
"uniqueItems": True,
|
|
1147
|
+
"minItems": 1,
|
|
1148
|
+
"maxItems": len(contract.allowed_option_ids),
|
|
1149
|
+
}
|
|
1150
|
+
required.extend(
|
|
1151
|
+
[
|
|
1152
|
+
"effect_predictions",
|
|
1153
|
+
"recommended_option_families",
|
|
1154
|
+
"action_template",
|
|
1155
|
+
"falsification_condition",
|
|
1156
|
+
]
|
|
1157
|
+
)
|
|
1158
|
+
if contract.allowed_option_ids:
|
|
1159
|
+
required.append("recommended_option_ids")
|
|
1160
|
+
if contract.is_semantic_v3:
|
|
1161
|
+
required.extend(
|
|
1162
|
+
["insight_kind", "consumer_scopes", "factor_capabilities"]
|
|
1163
|
+
)
|
|
1164
|
+
return {
|
|
1165
|
+
"type": "object",
|
|
1166
|
+
"properties": {
|
|
1167
|
+
"insights": {
|
|
1168
|
+
"type": "array",
|
|
1169
|
+
"minItems": cls.min_insights,
|
|
1170
|
+
"maxItems": cls.max_insights,
|
|
1171
|
+
"items": {
|
|
1172
|
+
"type": "object",
|
|
1173
|
+
"properties": insight_properties,
|
|
1174
|
+
"required": required,
|
|
1175
|
+
"additionalProperties": False,
|
|
1176
|
+
},
|
|
1177
|
+
}
|
|
1178
|
+
},
|
|
1179
|
+
"required": ["insights"],
|
|
1180
|
+
"additionalProperties": False,
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
|
|
1184
|
+
class _SourceAttributionOutput(BaseModel):
|
|
1185
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
1186
|
+
|
|
1187
|
+
path: _Path = Field(description=CANDIDATE_COMPONENT_PATH_CONTRACT)
|
|
1188
|
+
source: Literal["ancestor", "left", "right", "synthesized", "mutation"]
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
class _TwoParentSourceAttributionOutput(BaseModel):
|
|
1192
|
+
"""Operation-specific wire vocabulary accepted by executable crossover."""
|
|
1193
|
+
|
|
1194
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
1195
|
+
|
|
1196
|
+
path: _Path = Field(description=CANDIDATE_COMPONENT_PATH_CONTRACT)
|
|
1197
|
+
source: Literal["left", "right", "synthesized"]
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
class _ConflictResolutionOutput(BaseModel):
|
|
1201
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
1202
|
+
|
|
1203
|
+
relation_id: _RelationIdentifier
|
|
1204
|
+
choice: Literal["choose_left", "choose_right", "synthesize", "drop_both"]
|
|
1205
|
+
explanation: _ResolutionExplanation
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
class _ReflectionInsightOutput(BaseModel):
|
|
1209
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
1210
|
+
|
|
1211
|
+
claim: _ReflectionText
|
|
1212
|
+
trigger: _ReflectionText
|
|
1213
|
+
mechanism: _ReflectionText
|
|
1214
|
+
affected_paths: list[_Path] = Field(
|
|
1215
|
+
min_length=1,
|
|
1216
|
+
max_length=MAX_AFFECTED_PATHS,
|
|
1217
|
+
)
|
|
1218
|
+
evidence_summary: _ReflectionText
|
|
1219
|
+
evidence_contrast_ids: list[_ContrastIdentifier] = Field(
|
|
1220
|
+
max_length=MAX_EVIDENCE_CONTRAST_IDS,
|
|
1221
|
+
)
|
|
1222
|
+
confidence: float = Field(strict=True, ge=0.0, le=1.0, allow_inf_nan=False)
|
|
1223
|
+
|
|
1224
|
+
@field_validator("evidence_contrast_ids")
|
|
1225
|
+
@classmethod
|
|
1226
|
+
def _unique_evidence_contrast_ids(cls, values: list[str]) -> list[str]:
|
|
1227
|
+
del cls
|
|
1228
|
+
if len(set(values)) != len(values):
|
|
1229
|
+
raise ValueError("evidence_contrast_ids cannot contain duplicates")
|
|
1230
|
+
return values
|
|
1231
|
+
|
|
1232
|
+
|
|
1233
|
+
class _CatalogReflectionInsightOutput(BaseModel):
|
|
1234
|
+
"""Runtime shape for request-local citations before authenticated resolution."""
|
|
1235
|
+
|
|
1236
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
1237
|
+
|
|
1238
|
+
claim: _ReflectionText
|
|
1239
|
+
trigger: _ReflectionText
|
|
1240
|
+
mechanism: _ReflectionText
|
|
1241
|
+
affected_paths: list[_Path] = Field(
|
|
1242
|
+
min_length=1,
|
|
1243
|
+
max_length=MAX_AFFECTED_PATHS,
|
|
1244
|
+
)
|
|
1245
|
+
evidence_summary: _ReflectionText
|
|
1246
|
+
evidence_citation_keys: list[_EvidenceCitationKey] = Field(
|
|
1247
|
+
max_length=MAX_EVIDENCE_CONTRAST_IDS,
|
|
1248
|
+
)
|
|
1249
|
+
confidence: float = Field(strict=True, ge=0.0, le=1.0, allow_inf_nan=False)
|
|
1250
|
+
|
|
1251
|
+
@field_validator("evidence_citation_keys")
|
|
1252
|
+
@classmethod
|
|
1253
|
+
def _unique_evidence_citation_keys(cls, values: list[str]) -> list[str]:
|
|
1254
|
+
del cls
|
|
1255
|
+
if len(set(values)) != len(values):
|
|
1256
|
+
raise ValueError("evidence_citation_keys cannot contain duplicates")
|
|
1257
|
+
return values
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
class _MetricComparisonAnchorOutput(BaseModel):
|
|
1261
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
1262
|
+
|
|
1263
|
+
kind: Literal[
|
|
1264
|
+
"current_parent",
|
|
1265
|
+
"named_source_role",
|
|
1266
|
+
"common_ancestor",
|
|
1267
|
+
"frozen_archive_incumbent",
|
|
1268
|
+
]
|
|
1269
|
+
source_role_id: _Identifier | None = None
|
|
1270
|
+
|
|
1271
|
+
@model_validator(mode="after")
|
|
1272
|
+
def _role_matches_anchor_kind(self) -> "_MetricComparisonAnchorOutput":
|
|
1273
|
+
if self.kind == "named_source_role":
|
|
1274
|
+
if self.source_role_id is None:
|
|
1275
|
+
raise PydanticCustomError(
|
|
1276
|
+
ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
|
|
1277
|
+
"named_source_role anchors require a source_role_id",
|
|
1278
|
+
)
|
|
1279
|
+
elif self.source_role_id is not None:
|
|
1280
|
+
raise PydanticCustomError(
|
|
1281
|
+
ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
|
|
1282
|
+
"source_role_id is valid only for named_source_role anchors",
|
|
1283
|
+
)
|
|
1284
|
+
return self
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
class _MetricEffectPredictionOutput(BaseModel):
|
|
1288
|
+
model_config = _STRICT_MODEL_CONFIG
|
|
1289
|
+
|
|
1290
|
+
metric_id: _Identifier
|
|
1291
|
+
direction: Literal["decrease", "increase", "unchanged", "unknown"]
|
|
1292
|
+
comparison_anchor: _MetricComparisonAnchorOutput | None = None
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
def _validate_semantic_reflection_output(value: Any, output_type: type[Any]) -> None:
|
|
1296
|
+
"""Apply request-scoped semantic vocabularies without benchmark knowledge."""
|
|
1297
|
+
|
|
1298
|
+
semantic_fields_present = (
|
|
1299
|
+
value.insight_kind is not None
|
|
1300
|
+
or bool(value.consumer_scopes)
|
|
1301
|
+
or bool(value.factor_capabilities)
|
|
1302
|
+
or any(
|
|
1303
|
+
prediction.comparison_anchor is not None
|
|
1304
|
+
for prediction in value.effect_predictions
|
|
1305
|
+
)
|
|
1306
|
+
)
|
|
1307
|
+
if not output_type.semantic_v3:
|
|
1308
|
+
if semantic_fields_present:
|
|
1309
|
+
raise PydanticCustomError(
|
|
1310
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1311
|
+
"semantic fields require a v3 reflection insight contract",
|
|
1312
|
+
)
|
|
1313
|
+
return
|
|
1314
|
+
if value.insight_kind not in output_type.allowed_insight_kinds:
|
|
1315
|
+
raise PydanticCustomError(
|
|
1316
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1317
|
+
"insight_kind escaped the request vocabulary",
|
|
1318
|
+
)
|
|
1319
|
+
scopes = tuple(value.consumer_scopes)
|
|
1320
|
+
if not scopes or len(set(scopes)) != len(scopes):
|
|
1321
|
+
raise PydanticCustomError(
|
|
1322
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1323
|
+
"consumer_scopes must be nonempty and unique",
|
|
1324
|
+
)
|
|
1325
|
+
if not set(scopes).issubset(output_type.allowed_consumer_scopes):
|
|
1326
|
+
raise PydanticCustomError(
|
|
1327
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1328
|
+
"consumer_scopes escaped the request vocabulary",
|
|
1329
|
+
)
|
|
1330
|
+
paths = tuple(value.affected_paths)
|
|
1331
|
+
if len(set(paths)) != len(paths):
|
|
1332
|
+
raise PydanticCustomError(
|
|
1333
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1334
|
+
"affected_paths cannot contain duplicates",
|
|
1335
|
+
)
|
|
1336
|
+
if not set(paths).issubset(output_type.allowed_decision_paths):
|
|
1337
|
+
raise PydanticCustomError(
|
|
1338
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1339
|
+
"affected_paths escaped the decision-path vocabulary",
|
|
1340
|
+
)
|
|
1341
|
+
capabilities = tuple(value.factor_capabilities)
|
|
1342
|
+
if len(set(capabilities)) != len(capabilities):
|
|
1343
|
+
raise PydanticCustomError(
|
|
1344
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1345
|
+
"factor_capabilities cannot contain duplicates",
|
|
1346
|
+
)
|
|
1347
|
+
if not set(capabilities).issubset(output_type.allowed_factor_capabilities):
|
|
1348
|
+
raise PydanticCustomError(
|
|
1349
|
+
ValidationIssueReasonCode.REFLECTION_SEMANTIC_CONTRACT_VIOLATION.value,
|
|
1350
|
+
"factor_capabilities escaped the request vocabulary",
|
|
1351
|
+
)
|
|
1352
|
+
for prediction in value.effect_predictions:
|
|
1353
|
+
if prediction.direction == "unknown":
|
|
1354
|
+
raise PydanticCustomError(
|
|
1355
|
+
ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
|
|
1356
|
+
"v3 lifecycle hypotheses require an adjudicable metric direction",
|
|
1357
|
+
)
|
|
1358
|
+
anchor = prediction.comparison_anchor
|
|
1359
|
+
if anchor is None:
|
|
1360
|
+
raise PydanticCustomError(
|
|
1361
|
+
ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
|
|
1362
|
+
"v3 effect predictions require an explicit comparison_anchor",
|
|
1363
|
+
)
|
|
1364
|
+
if anchor.kind not in output_type.allowed_comparison_anchor_kinds:
|
|
1365
|
+
raise PydanticCustomError(
|
|
1366
|
+
ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
|
|
1367
|
+
"comparison anchor escaped the request vocabulary",
|
|
1368
|
+
)
|
|
1369
|
+
if (
|
|
1370
|
+
anchor.source_role_id is not None
|
|
1371
|
+
and anchor.source_role_id not in output_type.allowed_source_role_ids
|
|
1372
|
+
):
|
|
1373
|
+
raise PydanticCustomError(
|
|
1374
|
+
ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
|
|
1375
|
+
"comparison source role escaped the request vocabulary",
|
|
1376
|
+
)
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
def _validate_advanced_reflection_contract(
|
|
1380
|
+
value: Any,
|
|
1381
|
+
output_type: type[Any],
|
|
1382
|
+
) -> None:
|
|
1383
|
+
"""Validate the generic closed metric/action vocabulary with typed errors."""
|
|
1384
|
+
|
|
1385
|
+
metric_ids = tuple(item.metric_id for item in value.effect_predictions)
|
|
1386
|
+
if len(set(metric_ids)) != len(metric_ids) or set(metric_ids) != set(
|
|
1387
|
+
output_type.required_metric_ids
|
|
1388
|
+
):
|
|
1389
|
+
raise PydanticCustomError(
|
|
1390
|
+
ValidationIssueReasonCode.REFLECTION_METRIC_CONTRACT_VIOLATION.value,
|
|
1391
|
+
"effect_predictions must contain each required metric exactly once",
|
|
1392
|
+
)
|
|
1393
|
+
families = tuple(value.recommended_option_families)
|
|
1394
|
+
option_ids = tuple(value.recommended_option_ids)
|
|
1395
|
+
allowed_option_ids = output_type.allowed_option_ids
|
|
1396
|
+
action_invalid = (
|
|
1397
|
+
len(set(families)) != len(families)
|
|
1398
|
+
or not set(families).issubset(output_type.allowed_option_families)
|
|
1399
|
+
or len(set(option_ids)) != len(option_ids)
|
|
1400
|
+
or bool(allowed_option_ids) != bool(option_ids)
|
|
1401
|
+
or bool(option_ids) and not set(option_ids).issubset(allowed_option_ids)
|
|
1402
|
+
)
|
|
1403
|
+
if action_invalid:
|
|
1404
|
+
raise PydanticCustomError(
|
|
1405
|
+
ValidationIssueReasonCode.REFLECTION_ACTION_CONTRACT_VIOLATION.value,
|
|
1406
|
+
"recommended actions must be nonempty, unique, and request-scoped",
|
|
1407
|
+
)
|
|
1408
|
+
if all(item.direction == "unknown" for item in value.effect_predictions):
|
|
1409
|
+
raise PydanticCustomError(
|
|
1410
|
+
ValidationIssueReasonCode.REFLECTION_DIRECTION_OR_ANCHOR_VIOLATION.value,
|
|
1411
|
+
"an outcome-grounded insight must predict an adjudicable direction",
|
|
1412
|
+
)
|
|
1413
|
+
_validate_semantic_reflection_output(value, output_type)
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
class _AdvancedReflectionInsightOutput(_ReflectionInsightOutput):
|
|
1417
|
+
"""Strict runtime validator; the request supplies its closed vocabularies."""
|
|
1418
|
+
|
|
1419
|
+
required_metric_ids: ClassVar[tuple[str, ...]] = ()
|
|
1420
|
+
allowed_option_families: ClassVar[tuple[str, ...]] = ()
|
|
1421
|
+
allowed_option_ids: ClassVar[tuple[str, ...]] = ()
|
|
1422
|
+
allowed_decision_paths: ClassVar[tuple[str, ...]] = ()
|
|
1423
|
+
allowed_insight_kinds: ClassVar[tuple[str, ...]] = ()
|
|
1424
|
+
allowed_consumer_scopes: ClassVar[tuple[str, ...]] = ()
|
|
1425
|
+
allowed_comparison_anchor_kinds: ClassVar[tuple[str, ...]] = ()
|
|
1426
|
+
allowed_factor_capabilities: ClassVar[tuple[str, ...]] = ()
|
|
1427
|
+
allowed_source_role_ids: ClassVar[tuple[str, ...]] = ()
|
|
1428
|
+
semantic_v3: ClassVar[bool] = False
|
|
1429
|
+
|
|
1430
|
+
effect_predictions: list[_MetricEffectPredictionOutput]
|
|
1431
|
+
recommended_option_families: list[_Identifier] = Field(min_length=1)
|
|
1432
|
+
recommended_option_ids: list[_Identifier] = Field(default_factory=list)
|
|
1433
|
+
action_template: _ReflectionText
|
|
1434
|
+
falsification_condition: _ReflectionText
|
|
1435
|
+
insight_kind: _Identifier | None = None
|
|
1436
|
+
consumer_scopes: list[_Identifier] = Field(default_factory=list)
|
|
1437
|
+
factor_capabilities: list[_Identifier] = Field(default_factory=list)
|
|
1438
|
+
|
|
1439
|
+
@model_validator(mode="after")
|
|
1440
|
+
def _exact_advanced_contract(self) -> "_AdvancedReflectionInsightOutput":
|
|
1441
|
+
_validate_advanced_reflection_contract(self, type(self))
|
|
1442
|
+
return self
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
class _CatalogAdvancedReflectionInsightOutput(_CatalogReflectionInsightOutput):
|
|
1446
|
+
"""Advanced reflection contract over request-local evidence keys."""
|
|
1447
|
+
|
|
1448
|
+
required_metric_ids: ClassVar[tuple[str, ...]] = ()
|
|
1449
|
+
allowed_option_families: ClassVar[tuple[str, ...]] = ()
|
|
1450
|
+
allowed_option_ids: ClassVar[tuple[str, ...]] = ()
|
|
1451
|
+
allowed_decision_paths: ClassVar[tuple[str, ...]] = ()
|
|
1452
|
+
allowed_insight_kinds: ClassVar[tuple[str, ...]] = ()
|
|
1453
|
+
allowed_consumer_scopes: ClassVar[tuple[str, ...]] = ()
|
|
1454
|
+
allowed_comparison_anchor_kinds: ClassVar[tuple[str, ...]] = ()
|
|
1455
|
+
allowed_factor_capabilities: ClassVar[tuple[str, ...]] = ()
|
|
1456
|
+
allowed_source_role_ids: ClassVar[tuple[str, ...]] = ()
|
|
1457
|
+
semantic_v3: ClassVar[bool] = False
|
|
1458
|
+
|
|
1459
|
+
effect_predictions: list[_MetricEffectPredictionOutput]
|
|
1460
|
+
recommended_option_families: list[_Identifier] = Field(min_length=1)
|
|
1461
|
+
recommended_option_ids: list[_Identifier] = Field(default_factory=list)
|
|
1462
|
+
action_template: _ReflectionText
|
|
1463
|
+
falsification_condition: _ReflectionText
|
|
1464
|
+
insight_kind: _Identifier | None = None
|
|
1465
|
+
consumer_scopes: list[_Identifier] = Field(default_factory=list)
|
|
1466
|
+
factor_capabilities: list[_Identifier] = Field(default_factory=list)
|
|
1467
|
+
|
|
1468
|
+
@model_validator(mode="after")
|
|
1469
|
+
def _exact_advanced_contract(
|
|
1470
|
+
self,
|
|
1471
|
+
) -> "_CatalogAdvancedReflectionInsightOutput":
|
|
1472
|
+
_validate_advanced_reflection_contract(self, type(self))
|
|
1473
|
+
return self
|
|
1474
|
+
|
|
1475
|
+
|
|
1476
|
+
ResponseT = TypeVar("ResponseT")
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
@dataclass(frozen=True, slots=True)
|
|
1480
|
+
class AttemptedStructuredGenerationResponse(Generic[ResponseT]):
|
|
1481
|
+
"""A successful structured response plus its outer queue attempt count.
|
|
1482
|
+
|
|
1483
|
+
A direct one-attempt runner can return ``StructuredGenerationResponse``
|
|
1484
|
+
unchanged. A queue-composition function can return this envelope so retry
|
|
1485
|
+
evidence is not lost while the shared one-attempt port remains truthful.
|
|
1486
|
+
"""
|
|
1487
|
+
|
|
1488
|
+
response: StructuredGenerationResponse[ResponseT]
|
|
1489
|
+
attempt_count: int
|
|
1490
|
+
|
|
1491
|
+
def __post_init__(self) -> None:
|
|
1492
|
+
if type(self.response) is not StructuredGenerationResponse:
|
|
1493
|
+
raise TypeError("response must be an exact StructuredGenerationResponse")
|
|
1494
|
+
StructuredGenerationResponse.__post_init__(self.response)
|
|
1495
|
+
if type(self.attempt_count) is not int or self.attempt_count <= 0:
|
|
1496
|
+
raise ValueError("attempt_count must be a positive exact integer")
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
LowLevelResult = (
|
|
1500
|
+
StructuredGenerationResponse[Any] | AttemptedStructuredGenerationResponse[Any]
|
|
1501
|
+
)
|
|
1502
|
+
LowLevelRunner = Callable[
|
|
1503
|
+
[StructuredGenerationRequest[Any]],
|
|
1504
|
+
Awaitable[LowLevelResult],
|
|
1505
|
+
]
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
def _candidate_proposal_type(
|
|
1509
|
+
candidate_model: type[BaseModel],
|
|
1510
|
+
operation: str,
|
|
1511
|
+
) -> type[BaseModel]:
|
|
1512
|
+
if not isinstance(candidate_model, type) or not issubclass(
|
|
1513
|
+
candidate_model, BaseModel
|
|
1514
|
+
):
|
|
1515
|
+
raise TypeError("candidate_model must be a Pydantic BaseModel subclass")
|
|
1516
|
+
if candidate_model is BaseModel or getattr(
|
|
1517
|
+
candidate_model, "__pydantic_root_model__", False
|
|
1518
|
+
):
|
|
1519
|
+
raise TypeError("candidate_model must describe an object configuration")
|
|
1520
|
+
if getattr(candidate_model, "__parameters__", ()):
|
|
1521
|
+
raise TypeError("candidate_model must be a concrete Pydantic model")
|
|
1522
|
+
if type(operation) is not str or not operation.strip():
|
|
1523
|
+
raise ValueError("operation must be non-empty")
|
|
1524
|
+
|
|
1525
|
+
is_two_parent_crossover = operation == "two_parent_crossover"
|
|
1526
|
+
source_attribution_output = (
|
|
1527
|
+
_TwoParentSourceAttributionOutput
|
|
1528
|
+
if is_two_parent_crossover
|
|
1529
|
+
else _SourceAttributionOutput
|
|
1530
|
+
)
|
|
1531
|
+
fields: dict[str, tuple[Any, Any]] = {
|
|
1532
|
+
"configuration": (
|
|
1533
|
+
candidate_model,
|
|
1534
|
+
Field(description=_CONFIGURATION_ROOT_DESCRIPTION),
|
|
1535
|
+
),
|
|
1536
|
+
"design_rationale": (
|
|
1537
|
+
_Rationale,
|
|
1538
|
+
Field(description="Why this design should improve the stated objectives."),
|
|
1539
|
+
),
|
|
1540
|
+
"intended_changes": (
|
|
1541
|
+
list[_Change],
|
|
1542
|
+
Field(
|
|
1543
|
+
default_factory=list,
|
|
1544
|
+
max_length=MAX_INTENDED_CHANGES,
|
|
1545
|
+
description=(
|
|
1546
|
+
_TWO_PARENT_INTENDED_CHANGES_DESCRIPTION
|
|
1547
|
+
if is_two_parent_crossover
|
|
1548
|
+
else _INTENDED_CHANGES_DESCRIPTION
|
|
1549
|
+
),
|
|
1550
|
+
),
|
|
1551
|
+
),
|
|
1552
|
+
"source_attribution": (
|
|
1553
|
+
list[source_attribution_output],
|
|
1554
|
+
Field(
|
|
1555
|
+
default_factory=list,
|
|
1556
|
+
max_length=MAX_SOURCE_ATTRIBUTIONS,
|
|
1557
|
+
description=(
|
|
1558
|
+
_TWO_PARENT_SOURCE_ATTRIBUTION_DESCRIPTION
|
|
1559
|
+
if is_two_parent_crossover
|
|
1560
|
+
else _SOURCE_ATTRIBUTION_DESCRIPTION
|
|
1561
|
+
),
|
|
1562
|
+
),
|
|
1563
|
+
),
|
|
1564
|
+
"claimed_insight_ids": (
|
|
1565
|
+
list[_Identifier],
|
|
1566
|
+
Field(default_factory=list, max_length=MAX_CLAIMED_INSIGHT_IDS),
|
|
1567
|
+
),
|
|
1568
|
+
}
|
|
1569
|
+
# Strict three-way recombination is the only current operator with opaque
|
|
1570
|
+
# preservation receipts or conflict-resolution annotations. Omitting these
|
|
1571
|
+
# fields from mutation/crossover schemas prevents irrelevant, token-heavy
|
|
1572
|
+
# explanations from becoming an accidental second task.
|
|
1573
|
+
if operation not in {
|
|
1574
|
+
"typed_mutation",
|
|
1575
|
+
"two_parent_crossover",
|
|
1576
|
+
"three_way_recombination",
|
|
1577
|
+
"repair",
|
|
1578
|
+
}:
|
|
1579
|
+
fields["claimed_preservation_obligation_ids"] = (
|
|
1580
|
+
list[_Identifier],
|
|
1581
|
+
Field(default_factory=list, max_length=MAX_PRESERVATION_IDS),
|
|
1582
|
+
)
|
|
1583
|
+
fields["conflict_resolutions"] = (
|
|
1584
|
+
list[_ConflictResolutionOutput],
|
|
1585
|
+
Field(default_factory=list, max_length=MAX_CONFLICT_RESOLUTIONS),
|
|
1586
|
+
)
|
|
1587
|
+
elif operation == "three_way_recombination":
|
|
1588
|
+
fields["conflict_resolutions"] = (
|
|
1589
|
+
list[_ConflictResolutionOutput],
|
|
1590
|
+
Field(default_factory=list, max_length=MAX_CONFLICT_RESOLUTIONS),
|
|
1591
|
+
)
|
|
1592
|
+
return create_model(
|
|
1593
|
+
"CandidateProposal",
|
|
1594
|
+
__base__=_CompactCandidateSchemaBase,
|
|
1595
|
+
__module__=__name__,
|
|
1596
|
+
**fields,
|
|
1597
|
+
)
|
|
1598
|
+
|
|
1599
|
+
|
|
1600
|
+
def _atomic_mutation_proposal_type(
|
|
1601
|
+
candidate_model: type[BaseModel],
|
|
1602
|
+
operation: str,
|
|
1603
|
+
contract: AtomicMutationOutputContract,
|
|
1604
|
+
) -> type[BaseModel]:
|
|
1605
|
+
if not isinstance(candidate_model, type) or not issubclass(
|
|
1606
|
+
candidate_model, BaseModel
|
|
1607
|
+
):
|
|
1608
|
+
raise TypeError("candidate_model must be a Pydantic BaseModel subclass")
|
|
1609
|
+
if candidate_model is BaseModel or getattr(
|
|
1610
|
+
candidate_model, "__pydantic_root_model__", False
|
|
1611
|
+
):
|
|
1612
|
+
raise TypeError("candidate_model must describe an object configuration")
|
|
1613
|
+
if getattr(candidate_model, "__parameters__", ()):
|
|
1614
|
+
raise TypeError("candidate_model must be a concrete Pydantic model")
|
|
1615
|
+
if operation != "typed_mutation":
|
|
1616
|
+
raise ValueError("atomic mutation output is restricted to typed_mutation")
|
|
1617
|
+
if type(contract) is not AtomicMutationOutputContract:
|
|
1618
|
+
raise TypeError("contract must be an exact AtomicMutationOutputContract")
|
|
1619
|
+
AtomicMutationOutputContract.__post_init__(contract)
|
|
1620
|
+
|
|
1621
|
+
path_text = _path_text(contract.editable_path)
|
|
1622
|
+
path_literal = Literal.__getitem__((path_text,))
|
|
1623
|
+
output_type = create_model(
|
|
1624
|
+
"AtomicMutationProposal",
|
|
1625
|
+
__base__=_CompactAtomicMutationSchemaBase,
|
|
1626
|
+
__module__=__name__,
|
|
1627
|
+
path=(path_literal, Field(description="The exact contracted JSON path.")),
|
|
1628
|
+
replacement=(
|
|
1629
|
+
_AtomicScalar,
|
|
1630
|
+
Field(description="The replacement scalar at the contracted path."),
|
|
1631
|
+
),
|
|
1632
|
+
design_rationale=(
|
|
1633
|
+
_Rationale,
|
|
1634
|
+
Field(description="Why this one edit should improve the objectives."),
|
|
1635
|
+
),
|
|
1636
|
+
claimed_insight_ids=(
|
|
1637
|
+
list[_Identifier],
|
|
1638
|
+
Field(default_factory=list, max_length=MAX_CLAIMED_INSIGHT_IDS),
|
|
1639
|
+
),
|
|
1640
|
+
)
|
|
1641
|
+
output_type.expected_path_text = path_text
|
|
1642
|
+
leaf_schema = _atomic_leaf_schema(
|
|
1643
|
+
candidate_model,
|
|
1644
|
+
contract.editable_path,
|
|
1645
|
+
)
|
|
1646
|
+
current = value_at_path(
|
|
1647
|
+
contract.parent_configuration,
|
|
1648
|
+
contract.editable_path,
|
|
1649
|
+
)
|
|
1650
|
+
output_type.replacement_schema = _exclude_current_scalar(
|
|
1651
|
+
leaf_schema,
|
|
1652
|
+
current,
|
|
1653
|
+
)
|
|
1654
|
+
if contract.replacement_options:
|
|
1655
|
+
for option in contract.replacement_options:
|
|
1656
|
+
target = replace_existing_path(
|
|
1657
|
+
contract.parent_configuration,
|
|
1658
|
+
contract.editable_path,
|
|
1659
|
+
option,
|
|
1660
|
+
)
|
|
1661
|
+
validated = candidate_model.model_validate(
|
|
1662
|
+
thaw_json(target),
|
|
1663
|
+
strict=True,
|
|
1664
|
+
by_alias=False,
|
|
1665
|
+
by_name=True,
|
|
1666
|
+
)
|
|
1667
|
+
if not typed_json_equal(
|
|
1668
|
+
freeze_json(_configuration_dict(validated)),
|
|
1669
|
+
target,
|
|
1670
|
+
):
|
|
1671
|
+
raise ValueError(
|
|
1672
|
+
"candidate validation changed an atomic replacement option"
|
|
1673
|
+
)
|
|
1674
|
+
output_type.replacement_schema = _restrict_scalar_options(
|
|
1675
|
+
output_type.replacement_schema,
|
|
1676
|
+
contract.replacement_options,
|
|
1677
|
+
)
|
|
1678
|
+
output_type.candidate_model = candidate_model
|
|
1679
|
+
output_type.output_contract = contract
|
|
1680
|
+
return output_type
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
def _exact_parent_crossover_proposal_type(
|
|
1684
|
+
operation: str,
|
|
1685
|
+
contract: ExactParentCrossoverOutputContract,
|
|
1686
|
+
) -> type[BaseModel]:
|
|
1687
|
+
if operation != "two_parent_crossover":
|
|
1688
|
+
raise ValueError(
|
|
1689
|
+
"exact parent crossover output is restricted to two_parent_crossover"
|
|
1690
|
+
)
|
|
1691
|
+
if type(contract) is not ExactParentCrossoverOutputContract:
|
|
1692
|
+
raise TypeError("contract must be an exact ExactParentCrossoverOutputContract")
|
|
1693
|
+
ExactParentCrossoverOutputContract.__post_init__(contract)
|
|
1694
|
+
output_type = create_model(
|
|
1695
|
+
"ExactParentCrossoverPlan",
|
|
1696
|
+
__base__=_CompactExactParentCrossoverSchemaBase,
|
|
1697
|
+
__module__=__name__,
|
|
1698
|
+
import_locus_ids=(
|
|
1699
|
+
list[_Identifier],
|
|
1700
|
+
Field(min_length=1, max_length=len(contract.locus_ids) - 1),
|
|
1701
|
+
),
|
|
1702
|
+
claimed_insight_ids=(
|
|
1703
|
+
list[_Identifier],
|
|
1704
|
+
Field(
|
|
1705
|
+
default_factory=list,
|
|
1706
|
+
max_length=min(
|
|
1707
|
+
MAX_CLAIMED_INSIGHT_IDS,
|
|
1708
|
+
len(contract.claimable_insight_ids),
|
|
1709
|
+
),
|
|
1710
|
+
),
|
|
1711
|
+
),
|
|
1712
|
+
)
|
|
1713
|
+
output_type.allowed_locus_ids = contract.locus_ids
|
|
1714
|
+
output_type.claimable_insight_ids = contract.claimable_insight_ids
|
|
1715
|
+
output_type.forbidden_import_locus_sets = contract.forbidden_import_locus_sets
|
|
1716
|
+
return output_type
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
def _finite_variation_selection_type(
|
|
1720
|
+
candidate_model: type[BaseModel],
|
|
1721
|
+
operation: str,
|
|
1722
|
+
contract: FiniteVariationContract,
|
|
1723
|
+
) -> type[BaseModel]:
|
|
1724
|
+
"""Build one strict Literal-ID tool schema over prevalidated full children."""
|
|
1725
|
+
|
|
1726
|
+
if not isinstance(candidate_model, type) or not issubclass(
|
|
1727
|
+
candidate_model, BaseModel
|
|
1728
|
+
):
|
|
1729
|
+
raise TypeError("candidate_model must be a Pydantic BaseModel subclass")
|
|
1730
|
+
if candidate_model is BaseModel or getattr(
|
|
1731
|
+
candidate_model, "__pydantic_root_model__", False
|
|
1732
|
+
):
|
|
1733
|
+
raise TypeError("candidate_model must describe an object configuration")
|
|
1734
|
+
if getattr(candidate_model, "__parameters__", ()):
|
|
1735
|
+
raise TypeError("candidate_model must be a concrete Pydantic model")
|
|
1736
|
+
if type(operation) is not str or not operation.strip():
|
|
1737
|
+
raise ValueError("operation must be non-empty")
|
|
1738
|
+
validate_finite_variation_contract(contract)
|
|
1739
|
+
|
|
1740
|
+
for option in contract.options:
|
|
1741
|
+
validated = candidate_model.model_validate(
|
|
1742
|
+
thaw_json(option.child_configuration),
|
|
1743
|
+
strict=True,
|
|
1744
|
+
by_alias=False,
|
|
1745
|
+
by_name=True,
|
|
1746
|
+
)
|
|
1747
|
+
if not typed_json_equal(
|
|
1748
|
+
freeze_json(_configuration_dict(validated)),
|
|
1749
|
+
option.child_configuration,
|
|
1750
|
+
):
|
|
1751
|
+
raise ValueError("candidate validation changed a finite variation child")
|
|
1752
|
+
|
|
1753
|
+
option_ids = tuple(option.option_id for option in contract.options)
|
|
1754
|
+
option_literal = Literal.__getitem__(option_ids)
|
|
1755
|
+
output_type = create_model(
|
|
1756
|
+
"FiniteVariationSelectionProposal",
|
|
1757
|
+
__config__=_STRICT_MODEL_CONFIG,
|
|
1758
|
+
__module__=__name__,
|
|
1759
|
+
option_id=(
|
|
1760
|
+
option_literal,
|
|
1761
|
+
Field(description="The selected immutable variation option ID."),
|
|
1762
|
+
),
|
|
1763
|
+
design_rationale=(
|
|
1764
|
+
_Rationale,
|
|
1765
|
+
Field(description="Why this sealed option should improve the objectives."),
|
|
1766
|
+
),
|
|
1767
|
+
claimed_insight_ids=(
|
|
1768
|
+
list[_Identifier],
|
|
1769
|
+
Field(default_factory=list, max_length=MAX_CLAIMED_INSIGHT_IDS),
|
|
1770
|
+
),
|
|
1771
|
+
)
|
|
1772
|
+
return output_type
|
|
1773
|
+
|
|
1774
|
+
|
|
1775
|
+
def _reflection_output_type(
|
|
1776
|
+
max_insights: int,
|
|
1777
|
+
available_contrast_ids: tuple[str, ...] = (),
|
|
1778
|
+
insight_contract: ReflectionInsightContract | None = None,
|
|
1779
|
+
*,
|
|
1780
|
+
min_insights: int = 0,
|
|
1781
|
+
evidence_catalog: ReflectionEvidenceCatalog | None = None,
|
|
1782
|
+
) -> type[BaseModel]:
|
|
1783
|
+
if type(max_insights) is not int or not 1 <= max_insights <= 16:
|
|
1784
|
+
raise ValueError("max_insights must lie in [1,16]")
|
|
1785
|
+
if type(min_insights) is not int or not 0 <= min_insights <= max_insights:
|
|
1786
|
+
raise ValueError("min_insights must lie in [0,max_insights]")
|
|
1787
|
+
if type(available_contrast_ids) is not tuple:
|
|
1788
|
+
raise TypeError("available_contrast_ids must be an exact tuple")
|
|
1789
|
+
if evidence_catalog is not None:
|
|
1790
|
+
if type(evidence_catalog) is not ReflectionEvidenceCatalog:
|
|
1791
|
+
raise TypeError(
|
|
1792
|
+
"evidence_catalog must be an exact ReflectionEvidenceCatalog or None"
|
|
1793
|
+
)
|
|
1794
|
+
ReflectionEvidenceCatalog.__post_init__(evidence_catalog)
|
|
1795
|
+
if evidence_catalog.contrast_ids != available_contrast_ids:
|
|
1796
|
+
raise ValueError(
|
|
1797
|
+
"evidence_catalog must bind the exact available_contrast_ids"
|
|
1798
|
+
)
|
|
1799
|
+
citation_values = (
|
|
1800
|
+
evidence_catalog.citation_keys
|
|
1801
|
+
if evidence_catalog is not None
|
|
1802
|
+
else available_contrast_ids
|
|
1803
|
+
)
|
|
1804
|
+
if citation_values:
|
|
1805
|
+
allowed_contrast_id = Literal.__getitem__(citation_values)
|
|
1806
|
+
contrast_field = (
|
|
1807
|
+
list[allowed_contrast_id],
|
|
1808
|
+
Field(
|
|
1809
|
+
min_length=1,
|
|
1810
|
+
max_length=min(
|
|
1811
|
+
MAX_EVIDENCE_CONTRAST_IDS,
|
|
1812
|
+
len(citation_values),
|
|
1813
|
+
),
|
|
1814
|
+
),
|
|
1815
|
+
)
|
|
1816
|
+
else:
|
|
1817
|
+
contrast_field = (
|
|
1818
|
+
list[_ContrastIdentifier],
|
|
1819
|
+
Field(max_length=0),
|
|
1820
|
+
)
|
|
1821
|
+
if insight_contract is None:
|
|
1822
|
+
insight_base = (
|
|
1823
|
+
_CatalogReflectionInsightOutput
|
|
1824
|
+
if evidence_catalog is not None
|
|
1825
|
+
else _ReflectionInsightOutput
|
|
1826
|
+
)
|
|
1827
|
+
insight_type = create_model(
|
|
1828
|
+
"ReflectionInsightOutput",
|
|
1829
|
+
__base__=insight_base,
|
|
1830
|
+
__module__=__name__,
|
|
1831
|
+
**{
|
|
1832
|
+
(
|
|
1833
|
+
"evidence_citation_keys"
|
|
1834
|
+
if evidence_catalog is not None
|
|
1835
|
+
else "evidence_contrast_ids"
|
|
1836
|
+
): contrast_field
|
|
1837
|
+
},
|
|
1838
|
+
)
|
|
1839
|
+
else:
|
|
1840
|
+
if type(insight_contract) is not ReflectionInsightContract:
|
|
1841
|
+
raise TypeError(
|
|
1842
|
+
"insight_contract must be an exact ReflectionInsightContract"
|
|
1843
|
+
)
|
|
1844
|
+
ReflectionInsightContract.__post_init__(insight_contract)
|
|
1845
|
+
metric_id_literal = Literal.__getitem__(insight_contract.required_metric_ids)
|
|
1846
|
+
option_family_literal = Literal.__getitem__(
|
|
1847
|
+
insight_contract.allowed_option_families
|
|
1848
|
+
)
|
|
1849
|
+
metric_prediction_type = create_model(
|
|
1850
|
+
"MetricEffectPredictionOutput",
|
|
1851
|
+
__base__=_MetricEffectPredictionOutput,
|
|
1852
|
+
__module__=__name__,
|
|
1853
|
+
metric_id=(metric_id_literal, ...),
|
|
1854
|
+
)
|
|
1855
|
+
advanced_base = (
|
|
1856
|
+
_CatalogAdvancedReflectionInsightOutput
|
|
1857
|
+
if evidence_catalog is not None
|
|
1858
|
+
else _AdvancedReflectionInsightOutput
|
|
1859
|
+
)
|
|
1860
|
+
insight_type = create_model(
|
|
1861
|
+
"InterventionInsightOutput",
|
|
1862
|
+
__base__=advanced_base,
|
|
1863
|
+
__module__=__name__,
|
|
1864
|
+
**{
|
|
1865
|
+
(
|
|
1866
|
+
"evidence_citation_keys"
|
|
1867
|
+
if evidence_catalog is not None
|
|
1868
|
+
else "evidence_contrast_ids"
|
|
1869
|
+
): contrast_field,
|
|
1870
|
+
},
|
|
1871
|
+
effect_predictions=(
|
|
1872
|
+
list[metric_prediction_type],
|
|
1873
|
+
Field(
|
|
1874
|
+
min_length=len(insight_contract.required_metric_ids),
|
|
1875
|
+
max_length=len(insight_contract.required_metric_ids),
|
|
1876
|
+
),
|
|
1877
|
+
),
|
|
1878
|
+
recommended_option_families=(
|
|
1879
|
+
list[option_family_literal],
|
|
1880
|
+
Field(
|
|
1881
|
+
min_length=1,
|
|
1882
|
+
max_length=len(insight_contract.allowed_option_families),
|
|
1883
|
+
),
|
|
1884
|
+
),
|
|
1885
|
+
**(
|
|
1886
|
+
{}
|
|
1887
|
+
if not insight_contract.allowed_option_ids
|
|
1888
|
+
else {
|
|
1889
|
+
"recommended_option_ids": (
|
|
1890
|
+
list[Literal.__getitem__(insight_contract.allowed_option_ids)],
|
|
1891
|
+
Field(
|
|
1892
|
+
min_length=1,
|
|
1893
|
+
max_length=len(insight_contract.allowed_option_ids),
|
|
1894
|
+
),
|
|
1895
|
+
)
|
|
1896
|
+
}
|
|
1897
|
+
),
|
|
1898
|
+
)
|
|
1899
|
+
insight_type.required_metric_ids = insight_contract.required_metric_ids
|
|
1900
|
+
insight_type.allowed_option_families = insight_contract.allowed_option_families
|
|
1901
|
+
insight_type.allowed_option_ids = insight_contract.allowed_option_ids
|
|
1902
|
+
insight_type.allowed_decision_paths = insight_contract.allowed_decision_paths
|
|
1903
|
+
insight_type.allowed_insight_kinds = tuple(
|
|
1904
|
+
value.value for value in insight_contract.allowed_insight_kinds
|
|
1905
|
+
)
|
|
1906
|
+
insight_type.allowed_consumer_scopes = tuple(
|
|
1907
|
+
value.value for value in insight_contract.allowed_consumer_scopes
|
|
1908
|
+
)
|
|
1909
|
+
insight_type.allowed_comparison_anchor_kinds = tuple(
|
|
1910
|
+
value.value for value in insight_contract.allowed_comparison_anchor_kinds
|
|
1911
|
+
)
|
|
1912
|
+
insight_type.allowed_factor_capabilities = (
|
|
1913
|
+
insight_contract.allowed_factor_capabilities
|
|
1914
|
+
)
|
|
1915
|
+
insight_type.allowed_source_role_ids = insight_contract.allowed_source_role_ids
|
|
1916
|
+
insight_type.semantic_v3 = insight_contract.is_semantic_v3
|
|
1917
|
+
insights_field = (
|
|
1918
|
+
Field(default_factory=list, max_length=max_insights)
|
|
1919
|
+
if min_insights == 0
|
|
1920
|
+
else Field(
|
|
1921
|
+
min_length=min_insights,
|
|
1922
|
+
max_length=max_insights,
|
|
1923
|
+
)
|
|
1924
|
+
)
|
|
1925
|
+
output_type = create_model(
|
|
1926
|
+
"ReflectionOutput",
|
|
1927
|
+
__base__=_CompactReflectionSchemaBase,
|
|
1928
|
+
__module__=__name__,
|
|
1929
|
+
insights=(
|
|
1930
|
+
list[insight_type],
|
|
1931
|
+
insights_field,
|
|
1932
|
+
),
|
|
1933
|
+
)
|
|
1934
|
+
output_type.available_contrast_ids = available_contrast_ids
|
|
1935
|
+
output_type.available_evidence_citation_keys = (
|
|
1936
|
+
() if evidence_catalog is None else evidence_catalog.citation_keys
|
|
1937
|
+
)
|
|
1938
|
+
output_type.evidence_catalog_identity_sha256 = (
|
|
1939
|
+
None if evidence_catalog is None else evidence_catalog.catalog_identity_sha256
|
|
1940
|
+
)
|
|
1941
|
+
output_type.insight_contract = insight_contract
|
|
1942
|
+
output_type.min_insights = min_insights
|
|
1943
|
+
output_type.max_insights = max_insights
|
|
1944
|
+
return output_type
|
|
1945
|
+
|
|
1946
|
+
|
|
1947
|
+
def _validated_response(
|
|
1948
|
+
result: object,
|
|
1949
|
+
*,
|
|
1950
|
+
output_type: type[BaseModel],
|
|
1951
|
+
) -> tuple[StructuredGenerationResponse[Any], int]:
|
|
1952
|
+
if type(result) is AttemptedStructuredGenerationResponse:
|
|
1953
|
+
AttemptedStructuredGenerationResponse.__post_init__(result)
|
|
1954
|
+
response = result.response
|
|
1955
|
+
attempt_count = result.attempt_count
|
|
1956
|
+
elif type(result) is StructuredGenerationResponse:
|
|
1957
|
+
response = result
|
|
1958
|
+
attempt_count = 1
|
|
1959
|
+
else:
|
|
1960
|
+
raise TypeError(
|
|
1961
|
+
"low-level runner must return StructuredGenerationResponse or "
|
|
1962
|
+
"AttemptedStructuredGenerationResponse"
|
|
1963
|
+
)
|
|
1964
|
+
|
|
1965
|
+
StructuredGenerationResponse.__post_init__(response)
|
|
1966
|
+
if type(response.value) is not output_type:
|
|
1967
|
+
raise TypeError(
|
|
1968
|
+
"low-level response value does not match its requested output type"
|
|
1969
|
+
)
|
|
1970
|
+
return response, attempt_count
|
|
1971
|
+
|
|
1972
|
+
|
|
1973
|
+
def _telemetry(
|
|
1974
|
+
response: StructuredGenerationResponse[Any],
|
|
1975
|
+
*,
|
|
1976
|
+
attempt_count: int,
|
|
1977
|
+
) -> AgenticCallTelemetry:
|
|
1978
|
+
return AgenticCallTelemetry(
|
|
1979
|
+
requested_model=response.requested_model,
|
|
1980
|
+
resolved_model=response.resolved_model,
|
|
1981
|
+
resolved_provider=response.resolved_provider,
|
|
1982
|
+
provider_response_id=response.provider_response_id,
|
|
1983
|
+
finish_reason=response.finish_reason,
|
|
1984
|
+
input_tokens=response.input_tokens,
|
|
1985
|
+
output_tokens=response.output_tokens,
|
|
1986
|
+
reasoning_tokens=response.reasoning_tokens,
|
|
1987
|
+
cache_read_tokens=response.cache_read_tokens,
|
|
1988
|
+
cache_write_tokens=response.cache_write_tokens,
|
|
1989
|
+
cost_usd=response.cost_usd,
|
|
1990
|
+
latency_ns=response.latency_ns,
|
|
1991
|
+
attempt_count=attempt_count,
|
|
1992
|
+
)
|
|
1993
|
+
|
|
1994
|
+
|
|
1995
|
+
def _configuration_dict(configuration: BaseModel) -> dict[str, Any]:
|
|
1996
|
+
# Invoke BaseModel's implementation directly so a candidate cannot replace
|
|
1997
|
+
# this trust-boundary operation with an overriding method.
|
|
1998
|
+
value = BaseModel.model_dump(
|
|
1999
|
+
configuration,
|
|
2000
|
+
mode="python",
|
|
2001
|
+
by_alias=False,
|
|
2002
|
+
exclude_unset=False,
|
|
2003
|
+
exclude_defaults=False,
|
|
2004
|
+
exclude_none=False,
|
|
2005
|
+
exclude_computed_fields=True,
|
|
2006
|
+
round_trip=True,
|
|
2007
|
+
warnings="error",
|
|
2008
|
+
fallback=None,
|
|
2009
|
+
serialize_as_any=False,
|
|
2010
|
+
polymorphic_serialization=False,
|
|
2011
|
+
)
|
|
2012
|
+
if type(value) is not dict: # pragma: no cover - guarded by model admission.
|
|
2013
|
+
raise TypeError("candidate configuration must serialize to an exact dict")
|
|
2014
|
+
return value
|
|
2015
|
+
|
|
2016
|
+
|
|
2017
|
+
class PydanticAIAgenticGenerator:
|
|
2018
|
+
"""Map strict Pydantic outputs from an injected async runner into port values."""
|
|
2019
|
+
|
|
2020
|
+
def __init__(self, generate_once: LowLevelRunner) -> None:
|
|
2021
|
+
if not callable(generate_once):
|
|
2022
|
+
raise TypeError("generate_once must be callable")
|
|
2023
|
+
self._generate_once = generate_once
|
|
2024
|
+
|
|
2025
|
+
async def propose(
|
|
2026
|
+
self,
|
|
2027
|
+
request: VariationGenerationRequest,
|
|
2028
|
+
) -> VariationGenerationResult:
|
|
2029
|
+
if type(request) is not VariationGenerationRequest:
|
|
2030
|
+
raise TypeError("request must be an exact VariationGenerationRequest")
|
|
2031
|
+
VariationGenerationRequest.__post_init__(request)
|
|
2032
|
+
|
|
2033
|
+
atomic_contract = request.atomic_mutation_contract
|
|
2034
|
+
finite_contract = request.finite_variation_contract
|
|
2035
|
+
crossover_contract = request.exact_parent_crossover_contract
|
|
2036
|
+
if (
|
|
2037
|
+
atomic_contract is None
|
|
2038
|
+
and finite_contract is None
|
|
2039
|
+
and crossover_contract is None
|
|
2040
|
+
):
|
|
2041
|
+
output_type = _candidate_proposal_type(
|
|
2042
|
+
request.candidate_model,
|
|
2043
|
+
request.operation,
|
|
2044
|
+
)
|
|
2045
|
+
output_tool_name = CANDIDATE_PROPOSAL_TOOL_NAME
|
|
2046
|
+
elif atomic_contract is not None:
|
|
2047
|
+
output_type = _atomic_mutation_proposal_type(
|
|
2048
|
+
request.candidate_model,
|
|
2049
|
+
request.operation,
|
|
2050
|
+
atomic_contract,
|
|
2051
|
+
)
|
|
2052
|
+
output_tool_name = ATOMIC_MUTATION_TOOL_NAME
|
|
2053
|
+
elif crossover_contract is not None:
|
|
2054
|
+
output_type = _exact_parent_crossover_proposal_type(
|
|
2055
|
+
request.operation,
|
|
2056
|
+
crossover_contract,
|
|
2057
|
+
)
|
|
2058
|
+
output_tool_name = EXACT_PARENT_CROSSOVER_TOOL_NAME
|
|
2059
|
+
else:
|
|
2060
|
+
assert finite_contract is not None
|
|
2061
|
+
output_type = _finite_variation_selection_type(
|
|
2062
|
+
request.candidate_model,
|
|
2063
|
+
request.operation,
|
|
2064
|
+
finite_contract,
|
|
2065
|
+
)
|
|
2066
|
+
output_tool_name = FINITE_VARIATION_SELECTION_TOOL_NAME
|
|
2067
|
+
low_level_request = StructuredGenerationRequest(
|
|
2068
|
+
call_id=request.call_id,
|
|
2069
|
+
operation=request.operation,
|
|
2070
|
+
prompt=request.prompt,
|
|
2071
|
+
output_type=output_type,
|
|
2072
|
+
output_tool_name=output_tool_name,
|
|
2073
|
+
max_output_tokens=request.max_output_tokens,
|
|
2074
|
+
temperature=request.temperature,
|
|
2075
|
+
prompt_lineage=identity_prompt_lineage(request.prompt),
|
|
2076
|
+
)
|
|
2077
|
+
# Deliberately no exception handler: scheduler/provider errors retain
|
|
2078
|
+
# their original type, identity, and retry classification.
|
|
2079
|
+
low_level_result = await self._generate_once(low_level_request)
|
|
2080
|
+
response, attempt_count = _validated_response(
|
|
2081
|
+
low_level_result,
|
|
2082
|
+
output_type=output_type,
|
|
2083
|
+
)
|
|
2084
|
+
proposal = cast(Any, response.value)
|
|
2085
|
+
if atomic_contract is not None:
|
|
2086
|
+
replacement = freeze_json(proposal.replacement)
|
|
2087
|
+
draft: (
|
|
2088
|
+
CandidateDraft
|
|
2089
|
+
| AtomicMutationDraft
|
|
2090
|
+
| FiniteVariationSelectionDraft
|
|
2091
|
+
| ExactParentCrossoverDraft
|
|
2092
|
+
) = AtomicMutationDraft(
|
|
2093
|
+
path=atomic_contract.editable_path,
|
|
2094
|
+
replacement=replacement,
|
|
2095
|
+
design_rationale=proposal.design_rationale,
|
|
2096
|
+
claimed_insight_ids=tuple(proposal.claimed_insight_ids),
|
|
2097
|
+
)
|
|
2098
|
+
elif crossover_contract is not None:
|
|
2099
|
+
draft = ExactParentCrossoverDraft(
|
|
2100
|
+
contract_identity_sha256=(crossover_contract.contract_identity_sha256),
|
|
2101
|
+
import_locus_ids=tuple(sorted(proposal.import_locus_ids)),
|
|
2102
|
+
claimed_insight_ids=tuple(proposal.claimed_insight_ids),
|
|
2103
|
+
)
|
|
2104
|
+
elif finite_contract is not None:
|
|
2105
|
+
option = finite_contract.resolve(proposal.option_id)
|
|
2106
|
+
draft = FiniteVariationSelectionDraft(
|
|
2107
|
+
option_id=option.option_id,
|
|
2108
|
+
option_identity_sha256=option.identity_sha256,
|
|
2109
|
+
contract_identity_sha256=finite_contract.identity_sha256,
|
|
2110
|
+
design_rationale=proposal.design_rationale,
|
|
2111
|
+
claimed_insight_ids=tuple(proposal.claimed_insight_ids),
|
|
2112
|
+
)
|
|
2113
|
+
else:
|
|
2114
|
+
draft = CandidateDraft(
|
|
2115
|
+
configuration=_configuration_dict(proposal.configuration),
|
|
2116
|
+
design_rationale=proposal.design_rationale,
|
|
2117
|
+
intended_changes=tuple(proposal.intended_changes),
|
|
2118
|
+
source_attribution=tuple(
|
|
2119
|
+
SourceAttribution(path=item.path, source=item.source)
|
|
2120
|
+
for item in proposal.source_attribution
|
|
2121
|
+
),
|
|
2122
|
+
claimed_insight_ids=tuple(proposal.claimed_insight_ids),
|
|
2123
|
+
claimed_preservation_obligation_ids=tuple(
|
|
2124
|
+
getattr(proposal, "claimed_preservation_obligation_ids", ())
|
|
2125
|
+
),
|
|
2126
|
+
conflict_resolutions=tuple(
|
|
2127
|
+
ConflictResolutionDraft(
|
|
2128
|
+
relation_id=item.relation_id,
|
|
2129
|
+
choice=item.choice,
|
|
2130
|
+
explanation=item.explanation,
|
|
2131
|
+
)
|
|
2132
|
+
for item in getattr(proposal, "conflict_resolutions", ())
|
|
2133
|
+
),
|
|
2134
|
+
)
|
|
2135
|
+
return VariationGenerationResult(
|
|
2136
|
+
draft=draft,
|
|
2137
|
+
telemetry=_telemetry(response, attempt_count=attempt_count),
|
|
2138
|
+
)
|
|
2139
|
+
|
|
2140
|
+
async def reflect(
|
|
2141
|
+
self,
|
|
2142
|
+
request: ReflectionGenerationRequest,
|
|
2143
|
+
) -> ReflectionGenerationResult:
|
|
2144
|
+
if type(request) is not ReflectionGenerationRequest:
|
|
2145
|
+
raise TypeError("request must be an exact ReflectionGenerationRequest")
|
|
2146
|
+
ReflectionGenerationRequest.__post_init__(request)
|
|
2147
|
+
|
|
2148
|
+
output_type = _reflection_output_type(
|
|
2149
|
+
request.max_insights,
|
|
2150
|
+
request.available_contrast_ids,
|
|
2151
|
+
request.insight_contract,
|
|
2152
|
+
min_insights=request.min_insights,
|
|
2153
|
+
evidence_catalog=request.evidence_catalog,
|
|
2154
|
+
)
|
|
2155
|
+
low_level_request = StructuredGenerationRequest(
|
|
2156
|
+
call_id=request.call_id,
|
|
2157
|
+
operation=request.operation,
|
|
2158
|
+
prompt=render_reflection_prompt(
|
|
2159
|
+
request.prompt,
|
|
2160
|
+
request.evidence_catalog,
|
|
2161
|
+
insight_contract=request.insight_contract,
|
|
2162
|
+
),
|
|
2163
|
+
output_type=output_type,
|
|
2164
|
+
output_tool_name=REFLECTION_TOOL_NAME,
|
|
2165
|
+
max_output_tokens=request.max_output_tokens,
|
|
2166
|
+
temperature=request.temperature,
|
|
2167
|
+
prompt_lineage=StructuredPromptLineage(
|
|
2168
|
+
semantic_prompt_sha256=hashlib.sha256(
|
|
2169
|
+
request.prompt.encode("utf-8", errors="strict")
|
|
2170
|
+
).hexdigest(),
|
|
2171
|
+
renderer_id=REFLECTION_PROMPT_RENDERER_ID,
|
|
2172
|
+
renderer_revision=(
|
|
2173
|
+
REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION
|
|
2174
|
+
if request.insight_contract is not None
|
|
2175
|
+
and request.insight_contract.is_semantic_v3
|
|
2176
|
+
else (
|
|
2177
|
+
REFLECTION_PROMPT_RENDERER_REVISION
|
|
2178
|
+
if request.evidence_catalog is None
|
|
2179
|
+
else REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION
|
|
2180
|
+
)
|
|
2181
|
+
),
|
|
2182
|
+
renderer_definition_sha256=(
|
|
2183
|
+
REFLECTION_SEMANTIC_PROMPT_RENDERER_DEFINITION_SHA256
|
|
2184
|
+
if request.insight_contract is not None
|
|
2185
|
+
and request.insight_contract.is_semantic_v3
|
|
2186
|
+
else (
|
|
2187
|
+
REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256
|
|
2188
|
+
if request.evidence_catalog is None
|
|
2189
|
+
else (
|
|
2190
|
+
REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256
|
|
2191
|
+
)
|
|
2192
|
+
)
|
|
2193
|
+
),
|
|
2194
|
+
),
|
|
2195
|
+
)
|
|
2196
|
+
low_level_result = await self._generate_once(low_level_request)
|
|
2197
|
+
response, attempt_count = _validated_response(
|
|
2198
|
+
low_level_result,
|
|
2199
|
+
output_type=output_type,
|
|
2200
|
+
)
|
|
2201
|
+
reflection = cast(Any, response.value)
|
|
2202
|
+
|
|
2203
|
+
def resolved_evidence_ids(item: Any) -> tuple[str, ...]:
|
|
2204
|
+
catalog = request.evidence_catalog
|
|
2205
|
+
if catalog is None:
|
|
2206
|
+
return tuple(sorted(item.evidence_contrast_ids))
|
|
2207
|
+
citation_keys = tuple(item.evidence_citation_keys)
|
|
2208
|
+
return catalog.resolve_citation_keys(citation_keys)
|
|
2209
|
+
|
|
2210
|
+
insights = tuple(
|
|
2211
|
+
InsightDraft(
|
|
2212
|
+
claim=item.claim,
|
|
2213
|
+
trigger=item.trigger,
|
|
2214
|
+
mechanism=item.mechanism,
|
|
2215
|
+
# ``affected_paths`` is a set-like semantic field. Provider
|
|
2216
|
+
# order is not meaningful, while every downstream evidence
|
|
2217
|
+
# and audit identity requires canonical lexical order. Keep
|
|
2218
|
+
# duplicates intact so the semantic validator can reject them
|
|
2219
|
+
# instead of silently repairing invalid content.
|
|
2220
|
+
affected_paths=tuple(sorted(item.affected_paths)),
|
|
2221
|
+
evidence_summary=item.evidence_summary,
|
|
2222
|
+
confidence=float(item.confidence),
|
|
2223
|
+
evidence_contrast_ids=resolved_evidence_ids(item),
|
|
2224
|
+
effect_predictions=tuple(
|
|
2225
|
+
sorted(
|
|
2226
|
+
(
|
|
2227
|
+
MetricEffectPrediction(
|
|
2228
|
+
metric_id=prediction.metric_id,
|
|
2229
|
+
direction=MetricEffectDirection(prediction.direction),
|
|
2230
|
+
comparison_anchor=(
|
|
2231
|
+
None
|
|
2232
|
+
if prediction.comparison_anchor is None
|
|
2233
|
+
else MetricComparisonAnchor(
|
|
2234
|
+
kind=MetricComparisonAnchorKind(
|
|
2235
|
+
prediction.comparison_anchor.kind
|
|
2236
|
+
),
|
|
2237
|
+
source_role_id=(
|
|
2238
|
+
prediction.comparison_anchor.source_role_id
|
|
2239
|
+
),
|
|
2240
|
+
)
|
|
2241
|
+
),
|
|
2242
|
+
)
|
|
2243
|
+
for prediction in getattr(item, "effect_predictions", ())
|
|
2244
|
+
),
|
|
2245
|
+
key=lambda prediction: prediction.metric_id,
|
|
2246
|
+
)
|
|
2247
|
+
),
|
|
2248
|
+
recommended_option_families=tuple(
|
|
2249
|
+
sorted(getattr(item, "recommended_option_families", ()))
|
|
2250
|
+
),
|
|
2251
|
+
recommended_option_ids=tuple(
|
|
2252
|
+
sorted(getattr(item, "recommended_option_ids", ()))
|
|
2253
|
+
),
|
|
2254
|
+
action_template=getattr(item, "action_template", None),
|
|
2255
|
+
falsification_condition=getattr(item, "falsification_condition", None),
|
|
2256
|
+
insight_kind=(
|
|
2257
|
+
None
|
|
2258
|
+
if getattr(item, "insight_kind", None) is None
|
|
2259
|
+
else ReflectionInsightKind(item.insight_kind)
|
|
2260
|
+
),
|
|
2261
|
+
consumer_scopes=tuple(
|
|
2262
|
+
sorted(
|
|
2263
|
+
(
|
|
2264
|
+
ReflectionConsumerScope(value)
|
|
2265
|
+
for value in getattr(item, "consumer_scopes", ())
|
|
2266
|
+
),
|
|
2267
|
+
key=lambda value: value.value,
|
|
2268
|
+
)
|
|
2269
|
+
),
|
|
2270
|
+
factor_capabilities=tuple(
|
|
2271
|
+
sorted(getattr(item, "factor_capabilities", ()))
|
|
2272
|
+
),
|
|
2273
|
+
)
|
|
2274
|
+
for item in reflection.insights
|
|
2275
|
+
)
|
|
2276
|
+
result = ReflectionGenerationResult(
|
|
2277
|
+
insights=insights,
|
|
2278
|
+
telemetry=_telemetry(response, attempt_count=attempt_count),
|
|
2279
|
+
evidence_catalog_identity_sha256=(
|
|
2280
|
+
None
|
|
2281
|
+
if request.evidence_catalog is None
|
|
2282
|
+
else request.evidence_catalog.catalog_identity_sha256
|
|
2283
|
+
),
|
|
2284
|
+
)
|
|
2285
|
+
validate_reflection_evidence_catalog_result(request, result)
|
|
2286
|
+
return result
|
|
2287
|
+
|
|
2288
|
+
|
|
2289
|
+
__all__ = [
|
|
2290
|
+
"ATOMIC_MUTATION_TOOL_NAME",
|
|
2291
|
+
"AttemptedStructuredGenerationResponse",
|
|
2292
|
+
"CANDIDATE_PROPOSAL_TOOL_NAME",
|
|
2293
|
+
"FINITE_VARIATION_SELECTION_TOOL_NAME",
|
|
2294
|
+
"LowLevelRunner",
|
|
2295
|
+
"PydanticAIAgenticGenerator",
|
|
2296
|
+
"REFLECTION_OUTPUT_CONTRACT_NOTE",
|
|
2297
|
+
"REFLECTION_OUTPUT_CONTRACT_NOTE_SHA256",
|
|
2298
|
+
"REFLECTION_EVIDENCE_CATALOG_PROMPT_RENDERER_DEFINITION_SHA256",
|
|
2299
|
+
"REFLECTION_EVIDENCE_CATALOG_WIRE_CONTRACT_REVISION",
|
|
2300
|
+
"REFLECTION_PROMPT_RENDERER_DEFINITION_SHA256",
|
|
2301
|
+
"REFLECTION_PROMPT_RENDERER_ID",
|
|
2302
|
+
"REFLECTION_PROMPT_RENDERER_REVISION",
|
|
2303
|
+
"REFLECTION_SEMANTIC_PROMPT_RENDERER_DEFINITION_SHA256",
|
|
2304
|
+
"REFLECTION_SEMANTIC_WIRE_CONTRACT_REVISION",
|
|
2305
|
+
"REFLECTION_TOOL_NAME",
|
|
2306
|
+
"REFLECTION_WIRE_CONTRACT_REVISION",
|
|
2307
|
+
"render_reflection_prompt",
|
|
2308
|
+
]
|