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,734 @@
|
|
|
1
|
+
"""Provider-neutral port for one typed LLM generation attempt.
|
|
2
|
+
|
|
3
|
+
Retrying, queueing, and experiment policy deliberately live outside this port. An
|
|
4
|
+
adapter executes exactly one provider attempt and either returns typed telemetry or
|
|
5
|
+
raises :class:`StructuredGenerationError` with a closed, non-secret classification.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import math
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from decimal import Decimal
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from typing import Callable, Generic, Protocol, TypeVar, runtime_checkable
|
|
18
|
+
|
|
19
|
+
from agent_evolve.domain.ids import LLMCallId, ProviderAttemptId
|
|
20
|
+
from agent_evolve.domain.llm_task_queue import (
|
|
21
|
+
CanonicalProviderErrorCode,
|
|
22
|
+
MAX_VALIDATION_ISSUES,
|
|
23
|
+
SanitizedExceptionProvenance,
|
|
24
|
+
SanitizedValidationIssue,
|
|
25
|
+
StreamTimeoutPhase,
|
|
26
|
+
StructuredOutputFailureMode,
|
|
27
|
+
)
|
|
28
|
+
from agent_evolve.ports.generation_failure import GenerationFailureDisposition
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
OutputT = TypeVar("OutputT")
|
|
32
|
+
_OPERATION_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
33
|
+
_TOOL_TOKEN = re.compile(r"^[A-Za-z][A-Za-z0-9_-]{0,63}$")
|
|
34
|
+
_LOWER_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
|
35
|
+
_REPAIR_PATH_TOKEN = re.compile(r"^(?:\*|[A-Za-z_][A-Za-z0-9_-]{0,63})$")
|
|
36
|
+
MAX_PROMPT_UTF8_BYTES = 512_000
|
|
37
|
+
# Provider capabilities are injected by composition policies; this is only a
|
|
38
|
+
# language/runtime safety envelope for the integer carried across the generic
|
|
39
|
+
# port. In particular, it must not encode one model route's completion cap.
|
|
40
|
+
MAX_OUTPUT_TOKENS = 2_147_483_647
|
|
41
|
+
MAX_STRUCTURED_REPAIR_LITERAL_SETS = 8
|
|
42
|
+
MAX_STRUCTURED_REPAIR_LITERALS = 1_024
|
|
43
|
+
MAX_STRUCTURED_REPAIR_LITERAL_UTF8_BYTES = 256
|
|
44
|
+
MAX_STRUCTURED_REPAIR_CONTEXT_UTF8_BYTES = 16_384
|
|
45
|
+
DEFAULT_STREAM_CANCEL_DRAIN_TIMEOUT_NS = 5_000_000_000
|
|
46
|
+
DEFAULT_STREAM_TRANSPORT_RETIRE_TIMEOUT_NS = 5_000_000_000
|
|
47
|
+
STREAM_CLEANUP_POLICY_ID = "bounded_cancel_drain"
|
|
48
|
+
STREAM_CLEANUP_POLICY_VERSION = 1
|
|
49
|
+
STREAM_CLEANUP_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
50
|
+
b"agent-evolve:bounded-stream-cleanup:v1:"
|
|
51
|
+
b"cancel-on-liveness-expiry;wait-bounded-cancel-drain;"
|
|
52
|
+
b"if-unsettled-retire-supervisor-and-owned-transport-bounded;"
|
|
53
|
+
b"publish-terminal-nonretryable-cleanup-timeout;never-overlap-retry"
|
|
54
|
+
).hexdigest()
|
|
55
|
+
_STREAM_CLEANUP_CONFIGURATION_DOMAIN = (
|
|
56
|
+
b"agent-evolve:bounded-stream-cleanup-configuration:v1\x00"
|
|
57
|
+
)
|
|
58
|
+
IDENTITY_PROMPT_RENDERER_ID = "agent_evolve.identity_prompt"
|
|
59
|
+
IDENTITY_PROMPT_RENDERER_REVISION = "identity_v1"
|
|
60
|
+
IDENTITY_PROMPT_RENDERER_DEFINITION_SHA256 = hashlib.sha256(
|
|
61
|
+
b"agent-evolve:prompt-renderer:identity:v1\x00"
|
|
62
|
+
b"wire-prompt-utf8-bytes-equal-semantic-prompt-utf8-bytes"
|
|
63
|
+
).hexdigest()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class GenerationFailureKind(str, Enum):
|
|
67
|
+
"""Closed failure classes used by scheduler and trace projections."""
|
|
68
|
+
|
|
69
|
+
RATE_LIMITED = "rate_limited"
|
|
70
|
+
TIMEOUT = "timeout"
|
|
71
|
+
PROVIDER_UNAVAILABLE = "provider_unavailable"
|
|
72
|
+
CAPABILITY_MISMATCH = "capability_mismatch"
|
|
73
|
+
INVALID_REQUEST = "invalid_request"
|
|
74
|
+
AUTHENTICATION = "authentication"
|
|
75
|
+
PAYMENT_REQUIRED = "payment_required"
|
|
76
|
+
CONTENT_REJECTED = "content_rejected"
|
|
77
|
+
OUTPUT_INVALID = "output_invalid"
|
|
78
|
+
CANCELLED = "cancelled"
|
|
79
|
+
UNKNOWN = "unknown"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class StructuredStreamProgressKind(str, Enum):
|
|
83
|
+
"""Closed, content-free model-stream lifecycle events.
|
|
84
|
+
|
|
85
|
+
The part and output-selection values project provider-framework events.
|
|
86
|
+
``STREAM_COMPLETED`` is different: the adapter emits it locally only after
|
|
87
|
+
the supervised agent call has returned a typed result. No text,
|
|
88
|
+
reasoning, tool arguments, or candidate data may cross this boundary.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
PART_STARTED = "part_started"
|
|
92
|
+
PART_DELTA = "part_delta"
|
|
93
|
+
PART_ENDED = "part_ended"
|
|
94
|
+
OUTPUT_SELECTED = "output_selected"
|
|
95
|
+
STREAM_COMPLETED = "stream_completed"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class StructuredStreamChannel(str, Enum):
|
|
99
|
+
"""Closed stream channel classification safe for experiment journals."""
|
|
100
|
+
|
|
101
|
+
TEXT = "text"
|
|
102
|
+
THINKING = "thinking"
|
|
103
|
+
TOOL_CALL = "tool_call"
|
|
104
|
+
OTHER = "other"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
StructuredStreamTimeoutPhase = StreamTimeoutPhase
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True)
|
|
111
|
+
class StructuredStreamCleanupPolicy:
|
|
112
|
+
"""Bounded cleanup/retirement policy after a liveness deadline expires.
|
|
113
|
+
|
|
114
|
+
This is not a healthy-generation deadline. It begins only after first-event,
|
|
115
|
+
idle, absolute, or caller cancellation has already terminated the attempt.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
cancel_drain_timeout_ns: int = DEFAULT_STREAM_CANCEL_DRAIN_TIMEOUT_NS
|
|
119
|
+
transport_retire_timeout_ns: int = (
|
|
120
|
+
DEFAULT_STREAM_TRANSPORT_RETIRE_TIMEOUT_NS
|
|
121
|
+
)
|
|
122
|
+
policy_id: str = STREAM_CLEANUP_POLICY_ID
|
|
123
|
+
policy_version: int = STREAM_CLEANUP_POLICY_VERSION
|
|
124
|
+
definition_sha256: str = STREAM_CLEANUP_POLICY_DEFINITION_SHA256
|
|
125
|
+
|
|
126
|
+
def __post_init__(self) -> None:
|
|
127
|
+
for name in ("cancel_drain_timeout_ns", "transport_retire_timeout_ns"):
|
|
128
|
+
value = getattr(self, name)
|
|
129
|
+
if type(value) is not int or value <= 0:
|
|
130
|
+
raise ValueError(f"{name} must be a positive exact integer")
|
|
131
|
+
if type(self.policy_id) is not str or _OPERATION_TOKEN.fullmatch(
|
|
132
|
+
self.policy_id
|
|
133
|
+
) is None:
|
|
134
|
+
raise ValueError("policy_id must use the closed token grammar")
|
|
135
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
136
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
137
|
+
if (
|
|
138
|
+
type(self.definition_sha256) is not str
|
|
139
|
+
or _LOWER_SHA256.fullmatch(self.definition_sha256) is None
|
|
140
|
+
):
|
|
141
|
+
raise ValueError("definition_sha256 must be lowercase SHA-256")
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def configuration_sha256(self) -> str:
|
|
145
|
+
self.__post_init__()
|
|
146
|
+
payload = json.dumps(
|
|
147
|
+
{
|
|
148
|
+
"cancel_drain_timeout_ns": self.cancel_drain_timeout_ns,
|
|
149
|
+
"definition_sha256": self.definition_sha256,
|
|
150
|
+
"policy_id": self.policy_id,
|
|
151
|
+
"policy_version": self.policy_version,
|
|
152
|
+
"transport_retire_timeout_ns": self.transport_retire_timeout_ns,
|
|
153
|
+
},
|
|
154
|
+
allow_nan=False,
|
|
155
|
+
ensure_ascii=True,
|
|
156
|
+
separators=(",", ":"),
|
|
157
|
+
sort_keys=True,
|
|
158
|
+
).encode("ascii")
|
|
159
|
+
return hashlib.sha256(
|
|
160
|
+
_STREAM_CLEANUP_CONFIGURATION_DOMAIN + payload
|
|
161
|
+
).hexdigest()
|
|
162
|
+
|
|
163
|
+
def to_manifest_record(self) -> dict[str, object]:
|
|
164
|
+
self.__post_init__()
|
|
165
|
+
return {
|
|
166
|
+
"policy_id": self.policy_id,
|
|
167
|
+
"policy_version": self.policy_version,
|
|
168
|
+
"definition_sha256": self.definition_sha256,
|
|
169
|
+
"configuration_sha256": self.configuration_sha256,
|
|
170
|
+
"cancel_drain_timeout_ns": self.cancel_drain_timeout_ns,
|
|
171
|
+
"transport_retire_timeout_ns": self.transport_retire_timeout_ns,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@dataclass(frozen=True, slots=True)
|
|
176
|
+
class StructuredStreamLivenessPolicy:
|
|
177
|
+
"""Content-blind liveness policy for one streamed provider attempt.
|
|
178
|
+
|
|
179
|
+
``first_event_timeout_ns`` bounds an attempt that never starts producing
|
|
180
|
+
model-stream events. ``idle_timeout_ns`` is reset by every meaningful
|
|
181
|
+
event. ``absolute_timeout_ns`` is an optional operational fail-safe; it is
|
|
182
|
+
intentionally ``None`` by default so a healthy, progressing generation is
|
|
183
|
+
not truncated merely because it is long. ``cleanup_policy`` starts only
|
|
184
|
+
after one of those liveness boundaries (or caller cancellation) has fired.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
first_event_timeout_ns: int
|
|
188
|
+
idle_timeout_ns: int
|
|
189
|
+
absolute_timeout_ns: int | None = None
|
|
190
|
+
cleanup_policy: StructuredStreamCleanupPolicy = field(
|
|
191
|
+
default_factory=StructuredStreamCleanupPolicy
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def __post_init__(self) -> None:
|
|
195
|
+
for name in ("first_event_timeout_ns", "idle_timeout_ns"):
|
|
196
|
+
value = getattr(self, name)
|
|
197
|
+
if type(value) is not int or value <= 0:
|
|
198
|
+
raise ValueError(f"{name} must be a positive exact integer")
|
|
199
|
+
if self.absolute_timeout_ns is not None and (
|
|
200
|
+
type(self.absolute_timeout_ns) is not int
|
|
201
|
+
or self.absolute_timeout_ns <= 0
|
|
202
|
+
):
|
|
203
|
+
raise ValueError(
|
|
204
|
+
"absolute_timeout_ns must be a positive exact integer or None"
|
|
205
|
+
)
|
|
206
|
+
if type(self.cleanup_policy) is not StructuredStreamCleanupPolicy:
|
|
207
|
+
raise TypeError(
|
|
208
|
+
"cleanup_policy must be an exact StructuredStreamCleanupPolicy"
|
|
209
|
+
)
|
|
210
|
+
self.cleanup_policy.__post_init__()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@dataclass(frozen=True, slots=True)
|
|
214
|
+
class StructuredStreamProgress:
|
|
215
|
+
"""One content-free, monotonically sequenced stream observation.
|
|
216
|
+
|
|
217
|
+
The byte counters and rolling digest authenticate the adapter's exact
|
|
218
|
+
semantic UTF-8 fragments without retaining those fragments. They are not
|
|
219
|
+
transport/SSE byte counts. Lifecycle events legitimately carry a zero
|
|
220
|
+
delta and repeat the preceding rolling digest.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
call_id: str
|
|
224
|
+
sequence: int
|
|
225
|
+
kind: StructuredStreamProgressKind
|
|
226
|
+
channel: StructuredStreamChannel
|
|
227
|
+
elapsed_ns: int
|
|
228
|
+
event_content_utf8_bytes: int
|
|
229
|
+
cumulative_content_utf8_bytes: int
|
|
230
|
+
rolling_content_sha256: str
|
|
231
|
+
provider_attempt_id: str | None = None
|
|
232
|
+
|
|
233
|
+
def __post_init__(self) -> None:
|
|
234
|
+
LLMCallId(self.call_id)
|
|
235
|
+
if type(self.sequence) is not int or self.sequence <= 0:
|
|
236
|
+
raise ValueError("sequence must be a positive exact integer")
|
|
237
|
+
if type(self.kind) is not StructuredStreamProgressKind:
|
|
238
|
+
raise TypeError("kind must be a StructuredStreamProgressKind")
|
|
239
|
+
if type(self.channel) is not StructuredStreamChannel:
|
|
240
|
+
raise TypeError("channel must be a StructuredStreamChannel")
|
|
241
|
+
if type(self.elapsed_ns) is not int or self.elapsed_ns < 0:
|
|
242
|
+
raise ValueError("elapsed_ns must be a non-negative exact integer")
|
|
243
|
+
if (
|
|
244
|
+
type(self.event_content_utf8_bytes) is not int
|
|
245
|
+
or self.event_content_utf8_bytes < 0
|
|
246
|
+
):
|
|
247
|
+
raise ValueError(
|
|
248
|
+
"event_content_utf8_bytes must be a non-negative exact integer"
|
|
249
|
+
)
|
|
250
|
+
if self.kind in {
|
|
251
|
+
StructuredStreamProgressKind.OUTPUT_SELECTED,
|
|
252
|
+
StructuredStreamProgressKind.STREAM_COMPLETED,
|
|
253
|
+
} and (
|
|
254
|
+
self.channel is not StructuredStreamChannel.OTHER
|
|
255
|
+
or self.event_content_utf8_bytes != 0
|
|
256
|
+
):
|
|
257
|
+
raise ValueError(
|
|
258
|
+
"output selection and stream completion are content-free other events"
|
|
259
|
+
)
|
|
260
|
+
if (
|
|
261
|
+
type(self.cumulative_content_utf8_bytes) is not int
|
|
262
|
+
or self.cumulative_content_utf8_bytes < self.event_content_utf8_bytes
|
|
263
|
+
):
|
|
264
|
+
raise ValueError(
|
|
265
|
+
"cumulative_content_utf8_bytes must include the event byte count"
|
|
266
|
+
)
|
|
267
|
+
if (
|
|
268
|
+
type(self.rolling_content_sha256) is not str
|
|
269
|
+
or _LOWER_SHA256.fullmatch(self.rolling_content_sha256) is None
|
|
270
|
+
):
|
|
271
|
+
raise ValueError(
|
|
272
|
+
"rolling_content_sha256 must be a lowercase SHA-256 digest"
|
|
273
|
+
)
|
|
274
|
+
if self.provider_attempt_id is not None:
|
|
275
|
+
ProviderAttemptId(self.provider_attempt_id)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
StructuredStreamProgressSink = Callable[[StructuredStreamProgress], None]
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class StructuredGenerationError(RuntimeError):
|
|
282
|
+
"""A sanitized one-attempt failure safe to retain in experiment traces."""
|
|
283
|
+
|
|
284
|
+
def __init__(
|
|
285
|
+
self,
|
|
286
|
+
*,
|
|
287
|
+
kind: GenerationFailureKind,
|
|
288
|
+
retryable: bool,
|
|
289
|
+
safe_message: str,
|
|
290
|
+
status_code: int | None = None,
|
|
291
|
+
retry_after_seconds: float | None = None,
|
|
292
|
+
output_failure_mode: StructuredOutputFailureMode | None = None,
|
|
293
|
+
validation_issues: tuple[SanitizedValidationIssue, ...] = (),
|
|
294
|
+
provider_error_code: CanonicalProviderErrorCode | None = None,
|
|
295
|
+
provider_error_envelope_sha256: str | None = None,
|
|
296
|
+
exception_provenance: SanitizedExceptionProvenance | None = None,
|
|
297
|
+
) -> None:
|
|
298
|
+
if type(kind) is not GenerationFailureKind:
|
|
299
|
+
raise TypeError("kind must be a GenerationFailureKind")
|
|
300
|
+
if type(retryable) is not bool:
|
|
301
|
+
raise TypeError("retryable must be bool")
|
|
302
|
+
if type(safe_message) is not str or not safe_message.strip():
|
|
303
|
+
raise ValueError("safe_message must be non-empty")
|
|
304
|
+
if len(safe_message.encode("utf-8", errors="strict")) > 512:
|
|
305
|
+
raise ValueError("safe_message is too large for inline telemetry")
|
|
306
|
+
if status_code is not None and (
|
|
307
|
+
type(status_code) is not int or not 100 <= status_code <= 599
|
|
308
|
+
):
|
|
309
|
+
raise ValueError("status_code must be an HTTP status or None")
|
|
310
|
+
if retry_after_seconds is not None and (
|
|
311
|
+
isinstance(retry_after_seconds, bool)
|
|
312
|
+
or not isinstance(retry_after_seconds, (int, float))
|
|
313
|
+
or not math.isfinite(float(retry_after_seconds))
|
|
314
|
+
or float(retry_after_seconds) < 0
|
|
315
|
+
):
|
|
316
|
+
raise ValueError("retry_after_seconds must be finite and non-negative")
|
|
317
|
+
if (
|
|
318
|
+
output_failure_mode is not None
|
|
319
|
+
and type(output_failure_mode) is not StructuredOutputFailureMode
|
|
320
|
+
):
|
|
321
|
+
raise TypeError(
|
|
322
|
+
"output_failure_mode must be a StructuredOutputFailureMode or None"
|
|
323
|
+
)
|
|
324
|
+
if (
|
|
325
|
+
output_failure_mode is not None
|
|
326
|
+
and kind is not GenerationFailureKind.OUTPUT_INVALID
|
|
327
|
+
):
|
|
328
|
+
raise ValueError(
|
|
329
|
+
"only output_invalid failures may carry output diagnostics"
|
|
330
|
+
)
|
|
331
|
+
if type(validation_issues) is not tuple:
|
|
332
|
+
raise TypeError("validation_issues must be an exact tuple")
|
|
333
|
+
if len(validation_issues) > MAX_VALIDATION_ISSUES:
|
|
334
|
+
raise ValueError(
|
|
335
|
+
f"validation_issues cannot exceed {MAX_VALIDATION_ISSUES} entries"
|
|
336
|
+
)
|
|
337
|
+
if any(
|
|
338
|
+
type(issue) is not SanitizedValidationIssue for issue in validation_issues
|
|
339
|
+
):
|
|
340
|
+
raise TypeError(
|
|
341
|
+
"validation_issues must contain exact SanitizedValidationIssue values"
|
|
342
|
+
)
|
|
343
|
+
if output_failure_mode is None and validation_issues:
|
|
344
|
+
raise ValueError("validation issues require an output failure mode")
|
|
345
|
+
if (
|
|
346
|
+
validation_issues
|
|
347
|
+
and output_failure_mode is not StructuredOutputFailureMode.SCHEMA_VALIDATION
|
|
348
|
+
):
|
|
349
|
+
raise ValueError("validation issues require schema_validation mode")
|
|
350
|
+
if provider_error_code is not None and (
|
|
351
|
+
type(provider_error_code) is not CanonicalProviderErrorCode
|
|
352
|
+
):
|
|
353
|
+
raise TypeError(
|
|
354
|
+
"provider_error_code must be a CanonicalProviderErrorCode or None"
|
|
355
|
+
)
|
|
356
|
+
if provider_error_envelope_sha256 is not None and (
|
|
357
|
+
type(provider_error_envelope_sha256) is not str
|
|
358
|
+
or _LOWER_SHA256.fullmatch(provider_error_envelope_sha256) is None
|
|
359
|
+
):
|
|
360
|
+
raise ValueError(
|
|
361
|
+
"provider_error_envelope_sha256 must be lowercase SHA-256 or None"
|
|
362
|
+
)
|
|
363
|
+
if (
|
|
364
|
+
provider_error_code is not None
|
|
365
|
+
or provider_error_envelope_sha256 is not None
|
|
366
|
+
) and status_code is None:
|
|
367
|
+
raise ValueError("provider HTTP diagnostics require status_code")
|
|
368
|
+
if exception_provenance is not None and (
|
|
369
|
+
type(exception_provenance) is not SanitizedExceptionProvenance
|
|
370
|
+
):
|
|
371
|
+
raise TypeError(
|
|
372
|
+
"exception_provenance must be SanitizedExceptionProvenance or None"
|
|
373
|
+
)
|
|
374
|
+
if exception_provenance is not None:
|
|
375
|
+
SanitizedExceptionProvenance.__post_init__(exception_provenance)
|
|
376
|
+
if kind is not GenerationFailureKind.UNKNOWN:
|
|
377
|
+
raise ValueError(
|
|
378
|
+
"exception provenance is retained only for unknown failures"
|
|
379
|
+
)
|
|
380
|
+
super().__init__(safe_message)
|
|
381
|
+
self.kind = kind
|
|
382
|
+
self.retryable = retryable
|
|
383
|
+
self.safe_message = safe_message
|
|
384
|
+
self.status_code = status_code
|
|
385
|
+
self.retry_after_seconds = (
|
|
386
|
+
None if retry_after_seconds is None else float(retry_after_seconds)
|
|
387
|
+
)
|
|
388
|
+
self.output_failure_mode = output_failure_mode
|
|
389
|
+
self.validation_issues = validation_issues
|
|
390
|
+
self.provider_error_code = provider_error_code
|
|
391
|
+
self.provider_error_envelope_sha256 = provider_error_envelope_sha256
|
|
392
|
+
self.exception_provenance = exception_provenance
|
|
393
|
+
|
|
394
|
+
@property
|
|
395
|
+
def generation_failure_disposition(self) -> GenerationFailureDisposition:
|
|
396
|
+
if self.kind in {
|
|
397
|
+
GenerationFailureKind.OUTPUT_INVALID,
|
|
398
|
+
GenerationFailureKind.CONTENT_REJECTED,
|
|
399
|
+
}:
|
|
400
|
+
return GenerationFailureDisposition.MODEL_OR_SCHEMA_FAILURE
|
|
401
|
+
return GenerationFailureDisposition.INFRASTRUCTURE_FAILURE
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
class StructuredStreamTimeoutError(StructuredGenerationError, TimeoutError):
|
|
405
|
+
"""A content-blind stream liveness boundary expired before completion."""
|
|
406
|
+
|
|
407
|
+
def __init__(self, phase: StructuredStreamTimeoutPhase) -> None:
|
|
408
|
+
if type(phase) is not StructuredStreamTimeoutPhase:
|
|
409
|
+
raise TypeError("phase must be a StructuredStreamTimeoutPhase")
|
|
410
|
+
messages = {
|
|
411
|
+
StructuredStreamTimeoutPhase.FIRST_EVENT: (
|
|
412
|
+
"provider stream produced no first event before its liveness deadline"
|
|
413
|
+
),
|
|
414
|
+
StructuredStreamTimeoutPhase.IDLE: (
|
|
415
|
+
"provider stream stopped producing progress before completion"
|
|
416
|
+
),
|
|
417
|
+
StructuredStreamTimeoutPhase.ABSOLUTE: (
|
|
418
|
+
"provider stream exceeded its absolute operational fail-safe"
|
|
419
|
+
),
|
|
420
|
+
}
|
|
421
|
+
super().__init__(
|
|
422
|
+
kind=GenerationFailureKind.TIMEOUT,
|
|
423
|
+
retryable=True,
|
|
424
|
+
safe_message=messages[phase],
|
|
425
|
+
)
|
|
426
|
+
self.phase = phase
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class StructuredStreamCleanupTimeoutError(StructuredGenerationError, TimeoutError):
|
|
430
|
+
"""Cancellation did not settle; the attempt and transport are retired.
|
|
431
|
+
|
|
432
|
+
Unlike an ordinary liveness timeout this failure is terminal. Retrying could
|
|
433
|
+
overlap the still-running provider attempt whose cancellation was resisted.
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
def __init__(self, phase: StructuredStreamTimeoutPhase) -> None:
|
|
437
|
+
if type(phase) is not StructuredStreamTimeoutPhase:
|
|
438
|
+
raise TypeError("phase must be a StructuredStreamTimeoutPhase")
|
|
439
|
+
super().__init__(
|
|
440
|
+
kind=GenerationFailureKind.TIMEOUT,
|
|
441
|
+
retryable=False,
|
|
442
|
+
safe_message=(
|
|
443
|
+
"provider stream resisted bounded cancellation; its transport "
|
|
444
|
+
"was retired and retry is forbidden"
|
|
445
|
+
),
|
|
446
|
+
)
|
|
447
|
+
self.phase = phase
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class StructuredStreamRetiredError(StructuredGenerationError):
|
|
451
|
+
"""A supervisor/transport retired after an abandoned provider attempt."""
|
|
452
|
+
|
|
453
|
+
def __init__(self) -> None:
|
|
454
|
+
super().__init__(
|
|
455
|
+
kind=GenerationFailureKind.CANCELLED,
|
|
456
|
+
retryable=False,
|
|
457
|
+
safe_message=(
|
|
458
|
+
"provider stream supervisor is retired after incomplete cleanup"
|
|
459
|
+
),
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@dataclass(frozen=True, slots=True)
|
|
464
|
+
class StructuredPromptLineage:
|
|
465
|
+
"""Content-free commitment joining a semantic prompt to its wire renderer."""
|
|
466
|
+
|
|
467
|
+
semantic_prompt_sha256: str
|
|
468
|
+
renderer_id: str
|
|
469
|
+
renderer_revision: str
|
|
470
|
+
renderer_definition_sha256: str
|
|
471
|
+
|
|
472
|
+
def __post_init__(self) -> None:
|
|
473
|
+
for name in ("semantic_prompt_sha256", "renderer_definition_sha256"):
|
|
474
|
+
value = getattr(self, name)
|
|
475
|
+
if type(value) is not str or _LOWER_SHA256.fullmatch(value) is None:
|
|
476
|
+
raise ValueError(f"{name} must be a lowercase SHA-256 digest")
|
|
477
|
+
for name in ("renderer_id", "renderer_revision"):
|
|
478
|
+
value = getattr(self, name)
|
|
479
|
+
if type(value) is not str or _OPERATION_TOKEN.fullmatch(value) is None:
|
|
480
|
+
raise ValueError(
|
|
481
|
+
f"{name} must use the closed lowercase token grammar"
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def identity_prompt_lineage(prompt: str) -> StructuredPromptLineage:
|
|
486
|
+
"""Commit an exact prompt to the generic byte-identity renderer."""
|
|
487
|
+
|
|
488
|
+
if type(prompt) is not str or not prompt.strip():
|
|
489
|
+
raise ValueError("prompt must be a non-empty exact string")
|
|
490
|
+
prompt_bytes = prompt.encode("utf-8", errors="strict")
|
|
491
|
+
if len(prompt_bytes) > MAX_PROMPT_UTF8_BYTES:
|
|
492
|
+
raise ValueError("prompt exceeds MAX_PROMPT_UTF8_BYTES")
|
|
493
|
+
return StructuredPromptLineage(
|
|
494
|
+
semantic_prompt_sha256=hashlib.sha256(prompt_bytes).hexdigest(),
|
|
495
|
+
renderer_id=IDENTITY_PROMPT_RENDERER_ID,
|
|
496
|
+
renderer_revision=IDENTITY_PROMPT_RENDERER_REVISION,
|
|
497
|
+
renderer_definition_sha256=IDENTITY_PROMPT_RENDERER_DEFINITION_SHA256,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
@dataclass(frozen=True, slots=True)
|
|
502
|
+
class StructuredOutputRepairLiteralSet:
|
|
503
|
+
"""Provider-visible closed literals for a bounded structured-output repair.
|
|
504
|
+
|
|
505
|
+
This is a generic output-contract hint, not workload advice. A caller may
|
|
506
|
+
expose values that were already visible in the original provider request so
|
|
507
|
+
a retry can restate a large local closed set without weakening exact local
|
|
508
|
+
validation. No free-form text is accepted at this boundary.
|
|
509
|
+
"""
|
|
510
|
+
|
|
511
|
+
field_path: tuple[str, ...]
|
|
512
|
+
allowed_literals: tuple[str, ...]
|
|
513
|
+
|
|
514
|
+
def __post_init__(self) -> None:
|
|
515
|
+
if type(self.field_path) is not tuple or not 1 <= len(self.field_path) <= 8:
|
|
516
|
+
raise ValueError("field_path must contain between one and eight segments")
|
|
517
|
+
if any(
|
|
518
|
+
type(segment) is not str
|
|
519
|
+
or _REPAIR_PATH_TOKEN.fullmatch(segment) is None
|
|
520
|
+
for segment in self.field_path
|
|
521
|
+
):
|
|
522
|
+
raise ValueError("field_path segments violate the closed repair grammar")
|
|
523
|
+
if type(self.allowed_literals) is not tuple or not (
|
|
524
|
+
1 <= len(self.allowed_literals) <= MAX_STRUCTURED_REPAIR_LITERALS
|
|
525
|
+
):
|
|
526
|
+
raise ValueError(
|
|
527
|
+
"allowed_literals must be a non-empty bounded exact tuple"
|
|
528
|
+
)
|
|
529
|
+
if len(set(self.allowed_literals)) != len(self.allowed_literals):
|
|
530
|
+
raise ValueError("allowed_literals must be distinct")
|
|
531
|
+
for literal in self.allowed_literals:
|
|
532
|
+
if type(literal) is not str or not literal:
|
|
533
|
+
raise ValueError("allowed_literals must contain non-empty strings")
|
|
534
|
+
if (
|
|
535
|
+
len(literal.encode("utf-8", errors="strict"))
|
|
536
|
+
> MAX_STRUCTURED_REPAIR_LITERAL_UTF8_BYTES
|
|
537
|
+
):
|
|
538
|
+
raise ValueError("one repair literal exceeds its UTF-8 byte bound")
|
|
539
|
+
|
|
540
|
+
def to_record(self) -> dict[str, object]:
|
|
541
|
+
self.__post_init__()
|
|
542
|
+
return {
|
|
543
|
+
"field_path": list(self.field_path),
|
|
544
|
+
"allowed_literals": list(self.allowed_literals),
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
@dataclass(frozen=True, slots=True)
|
|
549
|
+
class StructuredGenerationRequest(Generic[OutputT]):
|
|
550
|
+
"""Complete application request for one typed logical LLM call."""
|
|
551
|
+
|
|
552
|
+
call_id: LLMCallId
|
|
553
|
+
operation: str
|
|
554
|
+
prompt: str
|
|
555
|
+
output_type: type[OutputT]
|
|
556
|
+
output_tool_name: str
|
|
557
|
+
max_output_tokens: int = 2_048
|
|
558
|
+
temperature: float | None = None
|
|
559
|
+
provider_attempt_id: ProviderAttemptId | None = None
|
|
560
|
+
prompt_lineage: StructuredPromptLineage | None = None
|
|
561
|
+
repair_literal_sets: tuple[StructuredOutputRepairLiteralSet, ...] = ()
|
|
562
|
+
|
|
563
|
+
def __post_init__(self) -> None:
|
|
564
|
+
if type(self.call_id) is not LLMCallId:
|
|
565
|
+
raise TypeError("call_id must be an exact LLMCallId")
|
|
566
|
+
LLMCallId.__post_init__(self.call_id)
|
|
567
|
+
if (
|
|
568
|
+
type(self.operation) is not str
|
|
569
|
+
or _OPERATION_TOKEN.fullmatch(self.operation) is None
|
|
570
|
+
):
|
|
571
|
+
raise ValueError("operation must use the closed lowercase token grammar")
|
|
572
|
+
if type(self.prompt) is not str or not self.prompt.strip():
|
|
573
|
+
raise ValueError("prompt must be a non-empty exact string")
|
|
574
|
+
if len(self.prompt.encode("utf-8", errors="strict")) > MAX_PROMPT_UTF8_BYTES:
|
|
575
|
+
raise ValueError("prompt exceeds MAX_PROMPT_UTF8_BYTES")
|
|
576
|
+
if not isinstance(self.output_type, type):
|
|
577
|
+
raise TypeError("output_type must be a runtime type")
|
|
578
|
+
if (
|
|
579
|
+
type(self.output_tool_name) is not str
|
|
580
|
+
or _TOOL_TOKEN.fullmatch(self.output_tool_name) is None
|
|
581
|
+
):
|
|
582
|
+
raise ValueError("output_tool_name must use the closed tool token grammar")
|
|
583
|
+
if (
|
|
584
|
+
type(self.max_output_tokens) is not int
|
|
585
|
+
or not 1 <= self.max_output_tokens <= MAX_OUTPUT_TOKENS
|
|
586
|
+
):
|
|
587
|
+
raise ValueError(f"max_output_tokens must lie in [1, {MAX_OUTPUT_TOKENS}]")
|
|
588
|
+
if self.temperature is not None and (
|
|
589
|
+
isinstance(self.temperature, bool)
|
|
590
|
+
or not isinstance(self.temperature, (int, float))
|
|
591
|
+
or not math.isfinite(float(self.temperature))
|
|
592
|
+
or not 0 <= float(self.temperature) <= 2
|
|
593
|
+
):
|
|
594
|
+
raise ValueError("temperature must be finite in [0,2] or None")
|
|
595
|
+
if self.provider_attempt_id is not None and (
|
|
596
|
+
type(self.provider_attempt_id) is not ProviderAttemptId
|
|
597
|
+
):
|
|
598
|
+
raise TypeError(
|
|
599
|
+
"provider_attempt_id must be a ProviderAttemptId or None"
|
|
600
|
+
)
|
|
601
|
+
if self.prompt_lineage is not None:
|
|
602
|
+
if type(self.prompt_lineage) is not StructuredPromptLineage:
|
|
603
|
+
raise TypeError(
|
|
604
|
+
"prompt_lineage must be an exact StructuredPromptLineage or None"
|
|
605
|
+
)
|
|
606
|
+
StructuredPromptLineage.__post_init__(self.prompt_lineage)
|
|
607
|
+
if self.prompt_lineage.renderer_id == IDENTITY_PROMPT_RENDERER_ID:
|
|
608
|
+
expected_semantic_sha256 = hashlib.sha256(
|
|
609
|
+
self.prompt.encode("utf-8", errors="strict")
|
|
610
|
+
).hexdigest()
|
|
611
|
+
if (
|
|
612
|
+
self.prompt_lineage.renderer_revision
|
|
613
|
+
!= IDENTITY_PROMPT_RENDERER_REVISION
|
|
614
|
+
or self.prompt_lineage.renderer_definition_sha256
|
|
615
|
+
!= IDENTITY_PROMPT_RENDERER_DEFINITION_SHA256
|
|
616
|
+
or self.prompt_lineage.semantic_prompt_sha256
|
|
617
|
+
!= expected_semantic_sha256
|
|
618
|
+
):
|
|
619
|
+
raise ValueError(
|
|
620
|
+
"identity prompt lineage does not authenticate the exact "
|
|
621
|
+
"wire prompt"
|
|
622
|
+
)
|
|
623
|
+
if type(self.repair_literal_sets) is not tuple or len(
|
|
624
|
+
self.repair_literal_sets
|
|
625
|
+
) > MAX_STRUCTURED_REPAIR_LITERAL_SETS:
|
|
626
|
+
raise ValueError("repair_literal_sets must be a bounded exact tuple")
|
|
627
|
+
if any(
|
|
628
|
+
type(item) is not StructuredOutputRepairLiteralSet
|
|
629
|
+
for item in self.repair_literal_sets
|
|
630
|
+
):
|
|
631
|
+
raise TypeError(
|
|
632
|
+
"repair_literal_sets must contain exact "
|
|
633
|
+
"StructuredOutputRepairLiteralSet values"
|
|
634
|
+
)
|
|
635
|
+
for item in self.repair_literal_sets:
|
|
636
|
+
item.__post_init__()
|
|
637
|
+
paths = tuple(item.field_path for item in self.repair_literal_sets)
|
|
638
|
+
if len(set(paths)) != len(paths):
|
|
639
|
+
raise ValueError("repair_literal_sets cannot repeat a field path")
|
|
640
|
+
repair_context_bytes = json.dumps(
|
|
641
|
+
[item.to_record() for item in self.repair_literal_sets],
|
|
642
|
+
allow_nan=False,
|
|
643
|
+
ensure_ascii=True,
|
|
644
|
+
separators=(",", ":"),
|
|
645
|
+
sort_keys=True,
|
|
646
|
+
).encode("ascii")
|
|
647
|
+
if len(repair_context_bytes) > MAX_STRUCTURED_REPAIR_CONTEXT_UTF8_BYTES:
|
|
648
|
+
raise ValueError("repair_literal_sets exceed the aggregate byte bound")
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
@dataclass(frozen=True, slots=True)
|
|
652
|
+
class StructuredGenerationResponse(Generic[OutputT]):
|
|
653
|
+
"""Typed output plus the telemetry available from exactly one attempt."""
|
|
654
|
+
|
|
655
|
+
value: OutputT
|
|
656
|
+
requested_model: str
|
|
657
|
+
resolved_model: str
|
|
658
|
+
resolved_provider: str
|
|
659
|
+
provider_response_id: str | None
|
|
660
|
+
finish_reason: str | None
|
|
661
|
+
input_tokens: int
|
|
662
|
+
output_tokens: int
|
|
663
|
+
reasoning_tokens: int
|
|
664
|
+
cache_read_tokens: int
|
|
665
|
+
cache_write_tokens: int
|
|
666
|
+
cost_usd: Decimal | None
|
|
667
|
+
latency_ns: int
|
|
668
|
+
|
|
669
|
+
def __post_init__(self) -> None:
|
|
670
|
+
for name in ("requested_model", "resolved_model", "resolved_provider"):
|
|
671
|
+
value = getattr(self, name)
|
|
672
|
+
if type(value) is not str or not value.strip():
|
|
673
|
+
raise ValueError(f"{name} must be a non-empty exact string")
|
|
674
|
+
for name in ("provider_response_id", "finish_reason"):
|
|
675
|
+
value = getattr(self, name)
|
|
676
|
+
if value is not None and (type(value) is not str or not value.strip()):
|
|
677
|
+
raise ValueError(f"{name} must be a non-empty exact string or None")
|
|
678
|
+
for name in (
|
|
679
|
+
"input_tokens",
|
|
680
|
+
"output_tokens",
|
|
681
|
+
"reasoning_tokens",
|
|
682
|
+
"cache_read_tokens",
|
|
683
|
+
"cache_write_tokens",
|
|
684
|
+
"latency_ns",
|
|
685
|
+
):
|
|
686
|
+
value = getattr(self, name)
|
|
687
|
+
if type(value) is not int or value < 0:
|
|
688
|
+
raise ValueError(f"{name} must be a non-negative exact integer")
|
|
689
|
+
if self.cost_usd is not None and (
|
|
690
|
+
type(self.cost_usd) is not Decimal
|
|
691
|
+
or not self.cost_usd.is_finite()
|
|
692
|
+
or self.cost_usd < 0
|
|
693
|
+
):
|
|
694
|
+
raise ValueError("cost_usd must be a finite non-negative Decimal or None")
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
@runtime_checkable
|
|
698
|
+
class StructuredGenerator(Protocol):
|
|
699
|
+
"""Execute one provider attempt; an outer scheduler owns all retries."""
|
|
700
|
+
|
|
701
|
+
async def generate_once(
|
|
702
|
+
self, request: StructuredGenerationRequest[OutputT]
|
|
703
|
+
) -> StructuredGenerationResponse[OutputT]: ...
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
__all__ = [
|
|
707
|
+
"DEFAULT_STREAM_CANCEL_DRAIN_TIMEOUT_NS",
|
|
708
|
+
"DEFAULT_STREAM_TRANSPORT_RETIRE_TIMEOUT_NS",
|
|
709
|
+
"GenerationFailureKind",
|
|
710
|
+
"IDENTITY_PROMPT_RENDERER_DEFINITION_SHA256",
|
|
711
|
+
"IDENTITY_PROMPT_RENDERER_ID",
|
|
712
|
+
"IDENTITY_PROMPT_RENDERER_REVISION",
|
|
713
|
+
"MAX_OUTPUT_TOKENS",
|
|
714
|
+
"MAX_PROMPT_UTF8_BYTES",
|
|
715
|
+
"STREAM_CLEANUP_POLICY_DEFINITION_SHA256",
|
|
716
|
+
"STREAM_CLEANUP_POLICY_ID",
|
|
717
|
+
"STREAM_CLEANUP_POLICY_VERSION",
|
|
718
|
+
"StructuredGenerationError",
|
|
719
|
+
"StructuredGenerationRequest",
|
|
720
|
+
"StructuredGenerationResponse",
|
|
721
|
+
"StructuredGenerator",
|
|
722
|
+
"StructuredPromptLineage",
|
|
723
|
+
"StructuredStreamChannel",
|
|
724
|
+
"StructuredStreamCleanupPolicy",
|
|
725
|
+
"StructuredStreamCleanupTimeoutError",
|
|
726
|
+
"StructuredStreamLivenessPolicy",
|
|
727
|
+
"StructuredStreamProgress",
|
|
728
|
+
"StructuredStreamProgressKind",
|
|
729
|
+
"StructuredStreamProgressSink",
|
|
730
|
+
"StructuredStreamRetiredError",
|
|
731
|
+
"StructuredStreamTimeoutError",
|
|
732
|
+
"StructuredStreamTimeoutPhase",
|
|
733
|
+
"identity_prompt_lineage",
|
|
734
|
+
]
|