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,205 @@
|
|
|
1
|
+
"""Fail-closed telemetry policy before a successful agentic call can be used.
|
|
2
|
+
|
|
3
|
+
The provider queue durably publishes terminal metadata first. This decorator
|
|
4
|
+
then verifies the scientific route and per-call resource envelope before an
|
|
5
|
+
``AgenticEvolutionEngine`` may materialize or physically evaluate the proposal.
|
|
6
|
+
Per-call aggregate ceilings avoid completion-order-dependent selective
|
|
7
|
+
acceptance: with a separate hard logical-call cap, their product is a
|
|
8
|
+
deterministic run ceiling. Reasoning usage is part of aggregate output usage;
|
|
9
|
+
an independent reasoning ceiling is enforced only when the selected route
|
|
10
|
+
actually guarantees one.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import hashlib
|
|
16
|
+
import json
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from decimal import Decimal
|
|
19
|
+
|
|
20
|
+
from agent_evolve.ports.agentic_generator import (
|
|
21
|
+
AgenticCallTelemetry,
|
|
22
|
+
AgenticGenerator,
|
|
23
|
+
ReflectionGenerationRequest,
|
|
24
|
+
ReflectionGenerationResult,
|
|
25
|
+
VariationGenerationRequest,
|
|
26
|
+
VariationGenerationResult,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AgenticTelemetryRejected(RuntimeError):
|
|
31
|
+
"""A sanitized rejection that never includes prompts or model output."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, reason: str) -> None:
|
|
34
|
+
if type(reason) is not str or not reason:
|
|
35
|
+
raise ValueError("rejection reason must be non-empty")
|
|
36
|
+
super().__init__(f"agentic response telemetry rejected: {reason}")
|
|
37
|
+
self.reason = reason
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _canonical_json(value: object) -> bytes:
|
|
41
|
+
return json.dumps(
|
|
42
|
+
value,
|
|
43
|
+
ensure_ascii=True,
|
|
44
|
+
allow_nan=False,
|
|
45
|
+
separators=(",", ":"),
|
|
46
|
+
sort_keys=True,
|
|
47
|
+
).encode("ascii")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True)
|
|
51
|
+
class AgenticTelemetryPolicy:
|
|
52
|
+
"""Exact provider/model identity and one-call token/cost bounds."""
|
|
53
|
+
|
|
54
|
+
requested_model: str
|
|
55
|
+
allowed_resolved_models: tuple[str, ...]
|
|
56
|
+
allowed_resolved_providers: tuple[str, ...]
|
|
57
|
+
max_cost_usd: Decimal
|
|
58
|
+
max_input_tokens: int
|
|
59
|
+
max_output_tokens: int
|
|
60
|
+
max_reasoning_tokens: int | None
|
|
61
|
+
max_attempt_count: int
|
|
62
|
+
|
|
63
|
+
policy_id = "exact_agentic_telemetry_gate"
|
|
64
|
+
policy_version = 3
|
|
65
|
+
reasoning_token_accounting = "included_in_output_tokens"
|
|
66
|
+
|
|
67
|
+
def __post_init__(self) -> None:
|
|
68
|
+
for name in ("requested_model",):
|
|
69
|
+
value = getattr(self, name)
|
|
70
|
+
if type(value) is not str or not value or value != value.strip():
|
|
71
|
+
raise ValueError(f"{name} must be canonical non-empty text")
|
|
72
|
+
for name in ("allowed_resolved_models", "allowed_resolved_providers"):
|
|
73
|
+
values = getattr(self, name)
|
|
74
|
+
if (
|
|
75
|
+
type(values) is not tuple
|
|
76
|
+
or not values
|
|
77
|
+
or any(
|
|
78
|
+
type(value) is not str or not value or value != value.strip()
|
|
79
|
+
for value in values
|
|
80
|
+
)
|
|
81
|
+
):
|
|
82
|
+
raise ValueError(f"{name} must be non-empty canonical text")
|
|
83
|
+
if len(set(values)) != len(values):
|
|
84
|
+
raise ValueError(f"{name} cannot contain duplicates")
|
|
85
|
+
if any("/" not in value for value in self.allowed_resolved_models):
|
|
86
|
+
raise ValueError("allowed_resolved_models must contain model slugs")
|
|
87
|
+
if type(self.max_cost_usd) is not Decimal:
|
|
88
|
+
raise TypeError("max_cost_usd must be an exact Decimal")
|
|
89
|
+
if not self.max_cost_usd.is_finite() or self.max_cost_usd < 0:
|
|
90
|
+
raise ValueError("max_cost_usd must be finite and non-negative")
|
|
91
|
+
for name in (
|
|
92
|
+
"max_input_tokens",
|
|
93
|
+
"max_output_tokens",
|
|
94
|
+
"max_attempt_count",
|
|
95
|
+
):
|
|
96
|
+
value = getattr(self, name)
|
|
97
|
+
if type(value) is not int or value < 0:
|
|
98
|
+
raise ValueError(f"{name} must be a non-negative integer")
|
|
99
|
+
if self.max_reasoning_tokens is not None and (
|
|
100
|
+
type(self.max_reasoning_tokens) is not int
|
|
101
|
+
or self.max_reasoning_tokens < 0
|
|
102
|
+
):
|
|
103
|
+
raise ValueError(
|
|
104
|
+
"max_reasoning_tokens must be a non-negative integer or None"
|
|
105
|
+
)
|
|
106
|
+
if self.max_attempt_count == 0:
|
|
107
|
+
raise ValueError("max_attempt_count must be positive")
|
|
108
|
+
|
|
109
|
+
def to_trace_record(self) -> dict[str, object]:
|
|
110
|
+
return {
|
|
111
|
+
"policy_id": self.policy_id,
|
|
112
|
+
"policy_version": self.policy_version,
|
|
113
|
+
"requested_model": self.requested_model,
|
|
114
|
+
"allowed_resolved_models": list(self.allowed_resolved_models),
|
|
115
|
+
"allowed_resolved_providers": list(self.allowed_resolved_providers),
|
|
116
|
+
"max_cost_usd": str(self.max_cost_usd),
|
|
117
|
+
"max_input_tokens": self.max_input_tokens,
|
|
118
|
+
"max_output_tokens": self.max_output_tokens,
|
|
119
|
+
"max_reasoning_tokens": self.max_reasoning_tokens,
|
|
120
|
+
"reasoning_token_accounting": self.reasoning_token_accounting,
|
|
121
|
+
"max_attempt_count": self.max_attempt_count,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def policy_sha256(self) -> str:
|
|
126
|
+
return hashlib.sha256(
|
|
127
|
+
b"agent-evolve:agentic-telemetry-policy:v3\x00"
|
|
128
|
+
+ _canonical_json(self.to_trace_record())
|
|
129
|
+
).hexdigest()
|
|
130
|
+
|
|
131
|
+
def validate(self, telemetry: AgenticCallTelemetry) -> None:
|
|
132
|
+
if type(telemetry) is not AgenticCallTelemetry:
|
|
133
|
+
raise TypeError("telemetry must be an exact AgenticCallTelemetry")
|
|
134
|
+
AgenticCallTelemetry.__post_init__(telemetry)
|
|
135
|
+
if telemetry.reasoning_tokens > telemetry.output_tokens:
|
|
136
|
+
raise AgenticTelemetryRejected("reasoning_output_accounting")
|
|
137
|
+
checks = (
|
|
138
|
+
(telemetry.requested_model == self.requested_model, "requested_model"),
|
|
139
|
+
(
|
|
140
|
+
telemetry.resolved_model in self.allowed_resolved_models,
|
|
141
|
+
"resolved_model",
|
|
142
|
+
),
|
|
143
|
+
(
|
|
144
|
+
telemetry.resolved_provider in self.allowed_resolved_providers,
|
|
145
|
+
"resolved_provider",
|
|
146
|
+
),
|
|
147
|
+
(telemetry.input_tokens <= self.max_input_tokens, "input_tokens"),
|
|
148
|
+
(telemetry.output_tokens <= self.max_output_tokens, "output_tokens"),
|
|
149
|
+
(telemetry.attempt_count <= self.max_attempt_count, "attempt_count"),
|
|
150
|
+
)
|
|
151
|
+
for accepted, reason in checks:
|
|
152
|
+
if not accepted:
|
|
153
|
+
raise AgenticTelemetryRejected(reason)
|
|
154
|
+
if (
|
|
155
|
+
self.max_reasoning_tokens is not None
|
|
156
|
+
and telemetry.reasoning_tokens > self.max_reasoning_tokens
|
|
157
|
+
):
|
|
158
|
+
raise AgenticTelemetryRejected("reasoning_tokens")
|
|
159
|
+
cost = telemetry.cost_usd
|
|
160
|
+
if cost is None:
|
|
161
|
+
raise AgenticTelemetryRejected("missing_cost")
|
|
162
|
+
if type(cost) is not Decimal:
|
|
163
|
+
raise AgenticTelemetryRejected("non_decimal_cost")
|
|
164
|
+
if not cost.is_finite() or cost < 0:
|
|
165
|
+
raise AgenticTelemetryRejected("invalid_cost")
|
|
166
|
+
if cost > self.max_cost_usd:
|
|
167
|
+
raise AgenticTelemetryRejected("cost_ceiling")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class TelemetryGatedAgenticGenerator:
|
|
171
|
+
"""Transparent generator decorator with a pre-evaluation telemetry gate."""
|
|
172
|
+
|
|
173
|
+
def __init__(self, generator: AgenticGenerator, policy: AgenticTelemetryPolicy):
|
|
174
|
+
if not isinstance(generator, AgenticGenerator):
|
|
175
|
+
raise TypeError("generator must implement AgenticGenerator")
|
|
176
|
+
if type(policy) is not AgenticTelemetryPolicy:
|
|
177
|
+
raise TypeError("policy must be an exact AgenticTelemetryPolicy")
|
|
178
|
+
AgenticTelemetryPolicy.__post_init__(policy)
|
|
179
|
+
self.generator = generator
|
|
180
|
+
self.policy = policy
|
|
181
|
+
|
|
182
|
+
async def propose(
|
|
183
|
+
self, request: VariationGenerationRequest
|
|
184
|
+
) -> VariationGenerationResult:
|
|
185
|
+
result = await self.generator.propose(request)
|
|
186
|
+
if type(result) is not VariationGenerationResult:
|
|
187
|
+
raise TypeError("generator returned a non-variation result")
|
|
188
|
+
self.policy.validate(result.telemetry)
|
|
189
|
+
return result
|
|
190
|
+
|
|
191
|
+
async def reflect(
|
|
192
|
+
self, request: ReflectionGenerationRequest
|
|
193
|
+
) -> ReflectionGenerationResult:
|
|
194
|
+
result = await self.generator.reflect(request)
|
|
195
|
+
if type(result) is not ReflectionGenerationResult:
|
|
196
|
+
raise TypeError("generator returned a non-reflection result")
|
|
197
|
+
self.policy.validate(result.telemetry)
|
|
198
|
+
return result
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
__all__ = [
|
|
202
|
+
"AgenticTelemetryPolicy",
|
|
203
|
+
"AgenticTelemetryRejected",
|
|
204
|
+
"TelemetryGatedAgenticGenerator",
|
|
205
|
+
]
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""Budgeted inter-generation feedback contracts.
|
|
2
|
+
|
|
3
|
+
The optimizer owns admission and accounting, while an injected interceptor owns
|
|
4
|
+
the feedback behavior (for example, trace reflection or memory curation). A
|
|
5
|
+
reservation is frozen before a generation starts. The interceptor is invoked
|
|
6
|
+
only after that generation's receipt has been sealed, so the next planner call
|
|
7
|
+
can observe its effects without allowing feedback to alter in-flight evidence.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import hashlib
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from agent_evolve.application.budgeted_optimizer import (
|
|
20
|
+
GenerationPlan,
|
|
21
|
+
GenerationReceipt,
|
|
22
|
+
OptimizerState,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_POLICY_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
27
|
+
_HASH_DOMAIN = b"agent-evolve:generation-feedback:v1\x00"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _canonical_json(value: object) -> bytes:
|
|
31
|
+
return json.dumps(
|
|
32
|
+
value,
|
|
33
|
+
ensure_ascii=True,
|
|
34
|
+
allow_nan=False,
|
|
35
|
+
separators=(",", ":"),
|
|
36
|
+
sort_keys=True,
|
|
37
|
+
).encode("ascii")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _record_hash(kind: str, value: object) -> str:
|
|
41
|
+
return hashlib.sha256(
|
|
42
|
+
_HASH_DOMAIN + kind.encode("ascii") + b"\x00" + _canonical_json(value)
|
|
43
|
+
).hexdigest()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _validate_metadata(value: tuple[tuple[str, str], ...], *, name: str) -> None:
|
|
47
|
+
if type(value) is not tuple:
|
|
48
|
+
raise TypeError(f"{name} must be an exact tuple")
|
|
49
|
+
for item in value:
|
|
50
|
+
if (
|
|
51
|
+
type(item) is not tuple
|
|
52
|
+
or len(item) != 2
|
|
53
|
+
or any(type(part) is not str or not part for part in item)
|
|
54
|
+
):
|
|
55
|
+
raise TypeError(f"{name} must contain non-empty exact string pairs")
|
|
56
|
+
if value != tuple(sorted(set(value))):
|
|
57
|
+
raise ValueError(f"{name} must be unique and canonically sorted")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True, slots=True)
|
|
61
|
+
class GenerationFeedbackReservation:
|
|
62
|
+
"""Pre-generation upper bound for one interceptor invocation."""
|
|
63
|
+
|
|
64
|
+
policy_id: str
|
|
65
|
+
policy_version: int
|
|
66
|
+
logical_llm_calls: int
|
|
67
|
+
metadata: tuple[tuple[str, str], ...] = ()
|
|
68
|
+
|
|
69
|
+
def __post_init__(self) -> None:
|
|
70
|
+
if (
|
|
71
|
+
type(self.policy_id) is not str
|
|
72
|
+
or _POLICY_ID.fullmatch(self.policy_id) is None
|
|
73
|
+
):
|
|
74
|
+
raise ValueError("policy_id must use the closed lowercase token grammar")
|
|
75
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
76
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
77
|
+
if type(self.logical_llm_calls) is not int or self.logical_llm_calls < 0:
|
|
78
|
+
raise ValueError("logical_llm_calls must be a non-negative exact integer")
|
|
79
|
+
_validate_metadata(self.metadata, name="metadata")
|
|
80
|
+
|
|
81
|
+
def to_record(self) -> dict[str, object]:
|
|
82
|
+
return {
|
|
83
|
+
"policy_id": self.policy_id,
|
|
84
|
+
"policy_version": self.policy_version,
|
|
85
|
+
"logical_llm_calls": self.logical_llm_calls,
|
|
86
|
+
"metadata": [list(item) for item in self.metadata],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def reservation_hash(self) -> str:
|
|
91
|
+
return _record_hash("reservation", self.to_record())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(frozen=True, slots=True)
|
|
95
|
+
class GenerationFeedbackContext:
|
|
96
|
+
"""Immutable post-generation evidence exposed to an interceptor."""
|
|
97
|
+
|
|
98
|
+
state: OptimizerState
|
|
99
|
+
plan: GenerationPlan
|
|
100
|
+
generation_receipt: GenerationReceipt
|
|
101
|
+
reservation: GenerationFeedbackReservation
|
|
102
|
+
|
|
103
|
+
def __post_init__(self) -> None:
|
|
104
|
+
if type(self.reservation) is not GenerationFeedbackReservation:
|
|
105
|
+
raise TypeError(
|
|
106
|
+
"reservation must be an exact GenerationFeedbackReservation"
|
|
107
|
+
)
|
|
108
|
+
if self.state.generation != self.plan.generation:
|
|
109
|
+
raise ValueError("feedback state and plan generations differ")
|
|
110
|
+
if self.generation_receipt.generation != self.plan.generation:
|
|
111
|
+
raise ValueError("feedback receipt and plan generations differ")
|
|
112
|
+
if not self.state.generation_receipts:
|
|
113
|
+
raise ValueError("feedback state has no sealed generation receipt")
|
|
114
|
+
if self.state.generation_receipts[-1] != self.generation_receipt:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
"feedback must observe the latest sealed generation receipt"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True, slots=True)
|
|
121
|
+
class GenerationFeedbackResult:
|
|
122
|
+
"""Interceptor-declared consumption and content-free result metadata."""
|
|
123
|
+
|
|
124
|
+
logical_llm_calls_used: int
|
|
125
|
+
metadata: tuple[tuple[str, str], ...] = ()
|
|
126
|
+
|
|
127
|
+
def __post_init__(self) -> None:
|
|
128
|
+
if (
|
|
129
|
+
type(self.logical_llm_calls_used) is not int
|
|
130
|
+
or self.logical_llm_calls_used < 0
|
|
131
|
+
):
|
|
132
|
+
raise ValueError(
|
|
133
|
+
"logical_llm_calls_used must be a non-negative exact integer"
|
|
134
|
+
)
|
|
135
|
+
_validate_metadata(self.metadata, name="metadata")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass(frozen=True, slots=True)
|
|
139
|
+
class GenerationFeedbackReceipt:
|
|
140
|
+
"""Authenticated accounting record for one completed interceptor call."""
|
|
141
|
+
|
|
142
|
+
generation: int
|
|
143
|
+
policy_id: str
|
|
144
|
+
policy_version: int
|
|
145
|
+
reservation_hash: str
|
|
146
|
+
generation_receipt_hash: str
|
|
147
|
+
logical_llm_calls_before: int
|
|
148
|
+
logical_llm_calls_after: int
|
|
149
|
+
reserved_logical_llm_calls: int
|
|
150
|
+
used_logical_llm_calls: int
|
|
151
|
+
result_metadata: tuple[tuple[str, str], ...]
|
|
152
|
+
receipt_hash: str
|
|
153
|
+
|
|
154
|
+
def __post_init__(self) -> None:
|
|
155
|
+
if type(self.generation) is not int or self.generation <= 0:
|
|
156
|
+
raise ValueError("generation must be a positive exact integer")
|
|
157
|
+
if (
|
|
158
|
+
type(self.policy_id) is not str
|
|
159
|
+
or _POLICY_ID.fullmatch(self.policy_id) is None
|
|
160
|
+
):
|
|
161
|
+
raise ValueError("policy_id must use the closed lowercase token grammar")
|
|
162
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
163
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
164
|
+
for name in ("reservation_hash", "generation_receipt_hash", "receipt_hash"):
|
|
165
|
+
value = getattr(self, name)
|
|
166
|
+
if type(value) is not str or re.fullmatch(r"[0-9a-f]{64}", value) is None:
|
|
167
|
+
raise ValueError(f"{name} must be a lowercase SHA-256 digest")
|
|
168
|
+
for name in (
|
|
169
|
+
"logical_llm_calls_before",
|
|
170
|
+
"logical_llm_calls_after",
|
|
171
|
+
"reserved_logical_llm_calls",
|
|
172
|
+
"used_logical_llm_calls",
|
|
173
|
+
):
|
|
174
|
+
value = getattr(self, name)
|
|
175
|
+
if type(value) is not int or value < 0:
|
|
176
|
+
raise ValueError(f"{name} must be a non-negative exact integer")
|
|
177
|
+
if self.used_logical_llm_calls > self.reserved_logical_llm_calls:
|
|
178
|
+
raise ValueError("used feedback calls cannot exceed the reservation")
|
|
179
|
+
if self.logical_llm_calls_after - self.logical_llm_calls_before != (
|
|
180
|
+
self.used_logical_llm_calls
|
|
181
|
+
):
|
|
182
|
+
raise ValueError("feedback counters differ from declared consumption")
|
|
183
|
+
_validate_metadata(self.result_metadata, name="result_metadata")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _receipt_record(receipt: GenerationFeedbackReceipt) -> dict[str, object]:
|
|
187
|
+
return {
|
|
188
|
+
"generation": receipt.generation,
|
|
189
|
+
"policy_id": receipt.policy_id,
|
|
190
|
+
"policy_version": receipt.policy_version,
|
|
191
|
+
"reservation_hash": receipt.reservation_hash,
|
|
192
|
+
"generation_receipt_hash": receipt.generation_receipt_hash,
|
|
193
|
+
"logical_llm_calls_before": receipt.logical_llm_calls_before,
|
|
194
|
+
"logical_llm_calls_after": receipt.logical_llm_calls_after,
|
|
195
|
+
"reserved_logical_llm_calls": receipt.reserved_logical_llm_calls,
|
|
196
|
+
"used_logical_llm_calls": receipt.used_logical_llm_calls,
|
|
197
|
+
"result_metadata": [list(item) for item in receipt.result_metadata],
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def generation_feedback_receipt_hash(receipt: GenerationFeedbackReceipt) -> str:
|
|
202
|
+
"""Recompute the canonical feedback receipt identity."""
|
|
203
|
+
|
|
204
|
+
if type(receipt) is not GenerationFeedbackReceipt:
|
|
205
|
+
raise TypeError("receipt must be an exact GenerationFeedbackReceipt")
|
|
206
|
+
return _record_hash("receipt", _receipt_record(receipt))
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def validate_generation_feedback_receipt(
|
|
210
|
+
receipt: GenerationFeedbackReceipt,
|
|
211
|
+
) -> None:
|
|
212
|
+
"""Fail closed unless a feedback receipt authenticates its contents."""
|
|
213
|
+
|
|
214
|
+
if type(receipt) is not GenerationFeedbackReceipt:
|
|
215
|
+
raise TypeError("receipt must be an exact GenerationFeedbackReceipt")
|
|
216
|
+
GenerationFeedbackReceipt.__post_init__(receipt)
|
|
217
|
+
if generation_feedback_receipt_hash(receipt) != receipt.receipt_hash:
|
|
218
|
+
raise ValueError("feedback receipt hash does not authenticate its contents")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def seal_generation_feedback(
|
|
222
|
+
*,
|
|
223
|
+
context: GenerationFeedbackContext,
|
|
224
|
+
result: GenerationFeedbackResult,
|
|
225
|
+
) -> GenerationFeedbackReceipt:
|
|
226
|
+
"""Validate exact consumption and seal the feedback accounting record."""
|
|
227
|
+
|
|
228
|
+
if type(context) is not GenerationFeedbackContext:
|
|
229
|
+
raise TypeError("context must be an exact GenerationFeedbackContext")
|
|
230
|
+
GenerationFeedbackContext.__post_init__(context)
|
|
231
|
+
if type(result) is not GenerationFeedbackResult:
|
|
232
|
+
raise TypeError("result must be an exact GenerationFeedbackResult")
|
|
233
|
+
GenerationFeedbackResult.__post_init__(result)
|
|
234
|
+
reservation = context.reservation
|
|
235
|
+
if result.logical_llm_calls_used > reservation.logical_llm_calls:
|
|
236
|
+
raise ValueError("feedback consumption exceeds its pre-generation reservation")
|
|
237
|
+
before = context.state.logical_llm_calls
|
|
238
|
+
after = before + result.logical_llm_calls_used
|
|
239
|
+
provisional = GenerationFeedbackReceipt(
|
|
240
|
+
generation=context.plan.generation,
|
|
241
|
+
policy_id=reservation.policy_id,
|
|
242
|
+
policy_version=reservation.policy_version,
|
|
243
|
+
reservation_hash=reservation.reservation_hash,
|
|
244
|
+
generation_receipt_hash=context.generation_receipt.receipt_hash,
|
|
245
|
+
logical_llm_calls_before=before,
|
|
246
|
+
logical_llm_calls_after=after,
|
|
247
|
+
reserved_logical_llm_calls=reservation.logical_llm_calls,
|
|
248
|
+
used_logical_llm_calls=result.logical_llm_calls_used,
|
|
249
|
+
result_metadata=result.metadata,
|
|
250
|
+
receipt_hash="0" * 64,
|
|
251
|
+
)
|
|
252
|
+
return GenerationFeedbackReceipt(
|
|
253
|
+
generation=provisional.generation,
|
|
254
|
+
policy_id=provisional.policy_id,
|
|
255
|
+
policy_version=provisional.policy_version,
|
|
256
|
+
reservation_hash=provisional.reservation_hash,
|
|
257
|
+
generation_receipt_hash=provisional.generation_receipt_hash,
|
|
258
|
+
logical_llm_calls_before=provisional.logical_llm_calls_before,
|
|
259
|
+
logical_llm_calls_after=provisional.logical_llm_calls_after,
|
|
260
|
+
reserved_logical_llm_calls=provisional.reserved_logical_llm_calls,
|
|
261
|
+
used_logical_llm_calls=provisional.used_logical_llm_calls,
|
|
262
|
+
result_metadata=provisional.result_metadata,
|
|
263
|
+
receipt_hash=_record_hash("receipt", _receipt_record(provisional)),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@runtime_checkable
|
|
268
|
+
class GenerationFeedbackInterceptor(Protocol):
|
|
269
|
+
"""Optional feedback behavior injected between sealed generations."""
|
|
270
|
+
|
|
271
|
+
def reserve(
|
|
272
|
+
self,
|
|
273
|
+
*,
|
|
274
|
+
state: OptimizerState,
|
|
275
|
+
plan: GenerationPlan,
|
|
276
|
+
) -> GenerationFeedbackReservation: ...
|
|
277
|
+
|
|
278
|
+
async def after_generation(
|
|
279
|
+
self,
|
|
280
|
+
context: GenerationFeedbackContext,
|
|
281
|
+
) -> GenerationFeedbackResult: ...
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
__all__ = [
|
|
285
|
+
"GenerationFeedbackContext",
|
|
286
|
+
"GenerationFeedbackInterceptor",
|
|
287
|
+
"GenerationFeedbackReceipt",
|
|
288
|
+
"GenerationFeedbackReservation",
|
|
289
|
+
"GenerationFeedbackResult",
|
|
290
|
+
"generation_feedback_receipt_hash",
|
|
291
|
+
"seal_generation_feedback",
|
|
292
|
+
"validate_generation_feedback_receipt",
|
|
293
|
+
]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""The durable side of a generative seal: one chained JSONL per campaign.
|
|
2
|
+
|
|
3
|
+
One line per model call, in issue order, each carrying the digest of the line
|
|
4
|
+
before it. Reading it back reconstructs the exact call sequence and re-derives
|
|
5
|
+
every digest, so a journal that has been edited, reordered, truncated at the
|
|
6
|
+
front, or had a call inserted into it fails to close.
|
|
7
|
+
|
|
8
|
+
The last property is the one that matters most here. An agent on this project
|
|
9
|
+
filled unevaluated compositions with predicted values and reported a 1.7x win
|
|
10
|
+
that had to be retracted. A chained journal makes the analogous move on the
|
|
11
|
+
proposal side impossible to do quietly: a call that did not happen has no
|
|
12
|
+
predecessor digest to inherit, and the terminal digest of the campaign is
|
|
13
|
+
published with the result.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import hashlib
|
|
19
|
+
import json
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Iterable, Sequence
|
|
22
|
+
|
|
23
|
+
from agent_evolve.domain.generative_emission import (
|
|
24
|
+
GenerativeEmission,
|
|
25
|
+
GenerativeProposalCall,
|
|
26
|
+
SealedGuidanceCall,
|
|
27
|
+
SealedRunHeader,
|
|
28
|
+
chain_sealed_calls,
|
|
29
|
+
)
|
|
30
|
+
from agent_evolve.domain.typed_json import freeze_json
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"candidate_schema_sha256",
|
|
34
|
+
"journal_line",
|
|
35
|
+
"read_generative_journal",
|
|
36
|
+
"verify_generative_journal",
|
|
37
|
+
"write_generative_journal",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
_SCHEMA_HASH_DOMAIN = b"agent-evolve:candidate-schema-identity:v1\x00"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def candidate_schema_sha256(candidate_model: Any) -> str:
|
|
44
|
+
"""Digest the exact JSON schema a proposal must satisfy.
|
|
45
|
+
|
|
46
|
+
This is the support of the generative operator, and therefore the support a
|
|
47
|
+
matched null has to sample. Recording it turns "the null draws from the same
|
|
48
|
+
space" from a claim in a write-up into a field two runs can be compared on.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
if candidate_model is None:
|
|
52
|
+
raise ValueError(
|
|
53
|
+
"a generative campaign needs a candidate schema. Without one the "
|
|
54
|
+
"proposer has no declared support and no null can be matched to it."
|
|
55
|
+
)
|
|
56
|
+
schema = candidate_model.model_json_schema()
|
|
57
|
+
payload = json.dumps(
|
|
58
|
+
schema, sort_keys=True, separators=(",", ":"), ensure_ascii=True
|
|
59
|
+
).encode("ascii")
|
|
60
|
+
return hashlib.sha256(_SCHEMA_HASH_DOMAIN + payload).hexdigest()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def journal_line(record: dict) -> str:
|
|
64
|
+
"""Serialise one sealed call as canonical JSON on a single line."""
|
|
65
|
+
|
|
66
|
+
return json.dumps(
|
|
67
|
+
record, sort_keys=True, separators=(",", ":"), ensure_ascii=True, allow_nan=False
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def write_generative_journal(path: Path, calls: Sequence[Any]) -> str:
|
|
72
|
+
"""Write the chain and return its terminal digest."""
|
|
73
|
+
|
|
74
|
+
terminal = chain_sealed_calls(tuple(calls))
|
|
75
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
with path.open("w", encoding="ascii") as handle:
|
|
77
|
+
for call in calls:
|
|
78
|
+
handle.write(journal_line(call.to_record()) + "\n")
|
|
79
|
+
return terminal
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _call_from_record(record: dict) -> Any:
|
|
83
|
+
if record.get("schema_version") != 1:
|
|
84
|
+
raise ValueError("unsupported sealed call schema_version")
|
|
85
|
+
kind = record.get("record_kind")
|
|
86
|
+
if kind == "run_header":
|
|
87
|
+
call = SealedRunHeader(
|
|
88
|
+
proposer_id=str(record["proposer_id"]),
|
|
89
|
+
requested_model=str(record["requested_model"]),
|
|
90
|
+
candidate_schema_sha256=str(record["candidate_schema_sha256"]),
|
|
91
|
+
provides_insights=bool(record["provides_insights"]),
|
|
92
|
+
)
|
|
93
|
+
elif "emissions" in record:
|
|
94
|
+
emissions = tuple(
|
|
95
|
+
GenerativeEmission(
|
|
96
|
+
configuration=freeze_json(dict(item["configuration"])),
|
|
97
|
+
accepted=bool(item["accepted"]),
|
|
98
|
+
rejection_reason=str(item.get("rejection_reason", "")),
|
|
99
|
+
)
|
|
100
|
+
for item in record["emissions"]
|
|
101
|
+
)
|
|
102
|
+
call = GenerativeProposalCall(
|
|
103
|
+
call_ordinal=int(record["call_ordinal"]),
|
|
104
|
+
op=str(record["op"]),
|
|
105
|
+
requested_model=str(record["requested_model"]),
|
|
106
|
+
prompt_sha256=str(record["prompt_sha256"]),
|
|
107
|
+
candidate_schema_sha256=str(record["candidate_schema_sha256"]),
|
|
108
|
+
emissions=emissions,
|
|
109
|
+
previous_call_sha256=str(record["previous_call_sha256"]),
|
|
110
|
+
)
|
|
111
|
+
elif "outputs" in record:
|
|
112
|
+
call = SealedGuidanceCall(
|
|
113
|
+
call_ordinal=int(record["call_ordinal"]),
|
|
114
|
+
op=str(record["op"]),
|
|
115
|
+
requested_model=str(record["requested_model"]),
|
|
116
|
+
prompt_sha256=str(record["prompt_sha256"]),
|
|
117
|
+
outputs=tuple(str(x) for x in record["outputs"]),
|
|
118
|
+
previous_call_sha256=str(record["previous_call_sha256"]),
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
raise ValueError("a sealed call carries either emissions or outputs")
|
|
122
|
+
if call.identity_sha256 != record["call_identity_sha256"]:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
f"call {record['call_ordinal']} does not authenticate: its recorded "
|
|
125
|
+
"identity is not the identity of its contents"
|
|
126
|
+
)
|
|
127
|
+
return call
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def read_generative_journal(path: Path) -> tuple:
|
|
131
|
+
"""Read a journal back into sealed call objects, authenticating each line."""
|
|
132
|
+
|
|
133
|
+
calls = []
|
|
134
|
+
with path.open("r", encoding="ascii") as handle:
|
|
135
|
+
for line in handle:
|
|
136
|
+
if not line.strip():
|
|
137
|
+
continue
|
|
138
|
+
calls.append(_call_from_record(json.loads(line)))
|
|
139
|
+
return tuple(calls)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def verify_generative_journal(path: Path) -> dict:
|
|
143
|
+
"""Authenticate a journal end to end and summarise what it says happened.
|
|
144
|
+
|
|
145
|
+
Every count here is derived from the sealed content. Nothing is declared.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
calls = read_generative_journal(path)
|
|
149
|
+
terminal = chain_sealed_calls(calls)
|
|
150
|
+
header = calls[0]
|
|
151
|
+
proposals = tuple(c for c in calls if type(c) is GenerativeProposalCall)
|
|
152
|
+
emitted = sum(len(c.emissions) for c in proposals)
|
|
153
|
+
accepted = sum(1 for c in proposals for e in c.emissions if e.accepted)
|
|
154
|
+
distinct = {
|
|
155
|
+
e.configuration_sha256 for c in proposals for e in c.emissions if e.accepted
|
|
156
|
+
}
|
|
157
|
+
schemas = {c.candidate_schema_sha256 for c in proposals}
|
|
158
|
+
models = {c.requested_model for c in calls}
|
|
159
|
+
return {
|
|
160
|
+
"path": str(path),
|
|
161
|
+
"terminal_sha256": terminal,
|
|
162
|
+
"proposer_id": header.proposer_id,
|
|
163
|
+
"provides_insights": header.provides_insights,
|
|
164
|
+
"calls": len(calls) - 1,
|
|
165
|
+
"proposal_calls": len(proposals),
|
|
166
|
+
"guidance_calls": len(calls) - 1 - len(proposals),
|
|
167
|
+
"emitted_configurations": emitted,
|
|
168
|
+
"accepted_configurations": accepted,
|
|
169
|
+
"distinct_accepted_configurations": len(distinct),
|
|
170
|
+
"candidate_schema_sha256s": sorted(schemas),
|
|
171
|
+
"requested_models": sorted(models),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def iter_accepted_configurations(calls: Iterable[Any]) -> tuple:
|
|
176
|
+
"""Every configuration the model authored that ``validate`` let through."""
|
|
177
|
+
|
|
178
|
+
out = []
|
|
179
|
+
for call in calls:
|
|
180
|
+
if type(call) is not GenerativeProposalCall:
|
|
181
|
+
continue
|
|
182
|
+
for emission in call.emissions:
|
|
183
|
+
if emission.accepted:
|
|
184
|
+
out.append(emission.configuration)
|
|
185
|
+
return tuple(out)
|