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,343 @@
|
|
|
1
|
+
"""Atomic, durable filesystem implementation of the artifact-store port."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import tempfile
|
|
7
|
+
import threading
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Iterator
|
|
11
|
+
|
|
12
|
+
import agent_evolve.domain.artifact as artifact_domain
|
|
13
|
+
from agent_evolve.domain.artifact import ArtifactRef
|
|
14
|
+
from agent_evolve.domain.ids import ArtifactId
|
|
15
|
+
from agent_evolve.infrastructure.artifacts._verification import (
|
|
16
|
+
require_artifact_id,
|
|
17
|
+
require_content_and_media_type,
|
|
18
|
+
verify_content,
|
|
19
|
+
verify_expected_media_type,
|
|
20
|
+
)
|
|
21
|
+
from agent_evolve.ports.artifact_store import (
|
|
22
|
+
ArtifactCollisionError,
|
|
23
|
+
ArtifactNotFoundError,
|
|
24
|
+
ArtifactSerializationError,
|
|
25
|
+
ArtifactTypeError,
|
|
26
|
+
CorruptArtifactError,
|
|
27
|
+
canonical_json_bytes,
|
|
28
|
+
decode_json_bytes,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
try: # pragma: no cover - exercised on Unix CI.
|
|
32
|
+
import fcntl
|
|
33
|
+
except ImportError: # pragma: no cover
|
|
34
|
+
fcntl = None
|
|
35
|
+
|
|
36
|
+
_ARTIFACT_SCHEMA_VERSION = 2
|
|
37
|
+
_MAGIC_LINE = b"AGENT_EVOLVE_ARTIFACT_V2"
|
|
38
|
+
_FILE_SUFFIX = ".artifact"
|
|
39
|
+
_METADATA_KEYS = {
|
|
40
|
+
"artifact_id",
|
|
41
|
+
"media_type",
|
|
42
|
+
"schema_version",
|
|
43
|
+
"sha256_hex",
|
|
44
|
+
"size_bytes",
|
|
45
|
+
}
|
|
46
|
+
_ROOT_LOCKS_GUARD = threading.Lock()
|
|
47
|
+
_ROOT_LOCKS: dict[str, threading.RLock] = {}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _process_lock_for_root(root: Path) -> threading.RLock:
|
|
51
|
+
"""Return one process-global lock for a canonical artifact-store root."""
|
|
52
|
+
|
|
53
|
+
lock_key = os.path.normcase(os.fspath(root))
|
|
54
|
+
with _ROOT_LOCKS_GUARD:
|
|
55
|
+
lock = _ROOT_LOCKS.get(lock_key)
|
|
56
|
+
if lock is None:
|
|
57
|
+
lock = threading.RLock()
|
|
58
|
+
_ROOT_LOCKS[lock_key] = lock
|
|
59
|
+
return lock
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class FileSystemArtifactStore:
|
|
63
|
+
"""Store one self-verifying ``<artifact_id>.artifact`` file per payload."""
|
|
64
|
+
|
|
65
|
+
def __init__(self, root: str | os.PathLike[str]) -> None:
|
|
66
|
+
requested_root = Path(root).absolute()
|
|
67
|
+
try:
|
|
68
|
+
canonical_parent = requested_root.parent.resolve(strict=True)
|
|
69
|
+
except FileNotFoundError as exc:
|
|
70
|
+
raise FileNotFoundError(
|
|
71
|
+
"Artifact-store parent directory must already exist: "
|
|
72
|
+
f"{requested_root.parent}"
|
|
73
|
+
) from exc
|
|
74
|
+
if not canonical_parent.is_dir():
|
|
75
|
+
raise NotADirectoryError(
|
|
76
|
+
f"Artifact-store parent is not a directory: {canonical_parent}"
|
|
77
|
+
)
|
|
78
|
+
canonical_root = canonical_parent / requested_root.name
|
|
79
|
+
self._process_lock = _process_lock_for_root(canonical_root)
|
|
80
|
+
with self._process_lock:
|
|
81
|
+
if canonical_root.is_symlink():
|
|
82
|
+
raise ValueError(
|
|
83
|
+
f"Artifact-store root must not be a symlink: {canonical_root}"
|
|
84
|
+
)
|
|
85
|
+
try:
|
|
86
|
+
canonical_root.mkdir(mode=0o700)
|
|
87
|
+
except FileExistsError:
|
|
88
|
+
if canonical_root.is_symlink():
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"Artifact-store root must not be a symlink: {canonical_root}"
|
|
91
|
+
)
|
|
92
|
+
if not canonical_root.is_dir():
|
|
93
|
+
raise NotADirectoryError(
|
|
94
|
+
f"Artifact-store root is not a directory: {canonical_root}"
|
|
95
|
+
)
|
|
96
|
+
# This is deliberately unconditional: if another process won a
|
|
97
|
+
# concurrent mkdir race, this constructor still establishes that the
|
|
98
|
+
# directory entry is durable before returning.
|
|
99
|
+
self._fsync_directory_path(canonical_parent)
|
|
100
|
+
|
|
101
|
+
resolved_root = canonical_root.resolve(strict=True)
|
|
102
|
+
if resolved_root != canonical_root:
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"Artifact-store root must resolve to itself: {canonical_root}"
|
|
105
|
+
)
|
|
106
|
+
self.root = resolved_root
|
|
107
|
+
self._lock_path = self.root / ".artifact-store.lock"
|
|
108
|
+
with self._locked():
|
|
109
|
+
self._verify_all_unlocked()
|
|
110
|
+
|
|
111
|
+
def path_for_artifact(self, artifact_id: ArtifactId) -> Path:
|
|
112
|
+
require_artifact_id(artifact_id)
|
|
113
|
+
return self.root / f"{artifact_id.value}{_FILE_SUFFIX}"
|
|
114
|
+
|
|
115
|
+
@contextmanager
|
|
116
|
+
def _locked(self) -> Iterator[None]:
|
|
117
|
+
with self._process_lock:
|
|
118
|
+
if fcntl is None:
|
|
119
|
+
# This remains safe between store instances and threads in this
|
|
120
|
+
# process. Cross-process use requires an available ``flock``.
|
|
121
|
+
yield
|
|
122
|
+
return
|
|
123
|
+
lock_fd = os.open(self._lock_path, os.O_RDWR | os.O_CREAT, 0o600)
|
|
124
|
+
locked = False
|
|
125
|
+
try:
|
|
126
|
+
# Failure is intentionally fail-closed: silently degrading here
|
|
127
|
+
# would make a cross-process deployment appear durable when the
|
|
128
|
+
# underlying filesystem does not support advisory locking.
|
|
129
|
+
fcntl.flock(lock_fd, fcntl.LOCK_EX)
|
|
130
|
+
locked = True
|
|
131
|
+
yield
|
|
132
|
+
finally:
|
|
133
|
+
try:
|
|
134
|
+
if locked:
|
|
135
|
+
fcntl.flock(lock_fd, fcntl.LOCK_UN)
|
|
136
|
+
finally:
|
|
137
|
+
os.close(lock_fd)
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def _metadata_record(ref: ArtifactRef) -> dict[str, Any]:
|
|
141
|
+
return {
|
|
142
|
+
"artifact_id": ref.artifact_id.value,
|
|
143
|
+
"media_type": ref.media_type,
|
|
144
|
+
"schema_version": _ARTIFACT_SCHEMA_VERSION,
|
|
145
|
+
"sha256_hex": ref.sha256_hex,
|
|
146
|
+
"size_bytes": ref.size_bytes,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def _encode_container(cls, ref: ArtifactRef, content: bytes) -> bytes:
|
|
151
|
+
metadata = canonical_json_bytes(cls._metadata_record(ref))
|
|
152
|
+
return _MAGIC_LINE + b"\n" + metadata + b"\n" + content
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def _decode_container(
|
|
156
|
+
cls,
|
|
157
|
+
encoded: bytes,
|
|
158
|
+
*,
|
|
159
|
+
path: Path,
|
|
160
|
+
expected_artifact_id: ArtifactId,
|
|
161
|
+
) -> tuple[ArtifactRef, bytes]:
|
|
162
|
+
parts = encoded.split(b"\n", 2)
|
|
163
|
+
if len(parts) != 3 or parts[0] != _MAGIC_LINE:
|
|
164
|
+
raise CorruptArtifactError(f"Artifact file has an invalid header: {path}")
|
|
165
|
+
raw_metadata, content = parts[1], parts[2]
|
|
166
|
+
try:
|
|
167
|
+
record = decode_json_bytes(raw_metadata)
|
|
168
|
+
except (ArtifactSerializationError, ArtifactTypeError) as exc:
|
|
169
|
+
raise CorruptArtifactError(
|
|
170
|
+
f"Artifact file has malformed metadata: {path}"
|
|
171
|
+
) from exc
|
|
172
|
+
if not isinstance(record, dict) or set(record) != _METADATA_KEYS:
|
|
173
|
+
raise CorruptArtifactError(
|
|
174
|
+
f"Artifact file has unsupported metadata fields: {path}"
|
|
175
|
+
)
|
|
176
|
+
try:
|
|
177
|
+
canonical_metadata = canonical_json_bytes(record)
|
|
178
|
+
except (ArtifactSerializationError, ArtifactTypeError) as exc:
|
|
179
|
+
raise CorruptArtifactError(
|
|
180
|
+
f"Artifact file metadata cannot be encoded canonically: {path}"
|
|
181
|
+
) from exc
|
|
182
|
+
if canonical_metadata != raw_metadata:
|
|
183
|
+
raise CorruptArtifactError(
|
|
184
|
+
f"Artifact file metadata is not canonical JSON: {path}"
|
|
185
|
+
)
|
|
186
|
+
schema_version = record["schema_version"]
|
|
187
|
+
if (
|
|
188
|
+
isinstance(schema_version, bool)
|
|
189
|
+
or not isinstance(schema_version, int)
|
|
190
|
+
or schema_version != _ARTIFACT_SCHEMA_VERSION
|
|
191
|
+
):
|
|
192
|
+
raise CorruptArtifactError(
|
|
193
|
+
f"Artifact file has an unsupported schema version: {path}"
|
|
194
|
+
)
|
|
195
|
+
try:
|
|
196
|
+
ref = ArtifactRef(
|
|
197
|
+
artifact_id=ArtifactId(record["artifact_id"]),
|
|
198
|
+
sha256_hex=record["sha256_hex"],
|
|
199
|
+
size_bytes=record["size_bytes"],
|
|
200
|
+
media_type=record["media_type"],
|
|
201
|
+
)
|
|
202
|
+
except (TypeError, ValueError) as exc:
|
|
203
|
+
raise CorruptArtifactError(
|
|
204
|
+
f"Artifact file metadata violates the schema: {path}"
|
|
205
|
+
) from exc
|
|
206
|
+
if ref.artifact_id != expected_artifact_id:
|
|
207
|
+
raise CorruptArtifactError(
|
|
208
|
+
f"Artifact file name does not match its metadata: {path}"
|
|
209
|
+
)
|
|
210
|
+
verify_content(ref, content)
|
|
211
|
+
return ref, content
|
|
212
|
+
|
|
213
|
+
def _read_path_unlocked(
|
|
214
|
+
self,
|
|
215
|
+
path: Path,
|
|
216
|
+
*,
|
|
217
|
+
expected_artifact_id: ArtifactId,
|
|
218
|
+
) -> tuple[ArtifactRef, bytes]:
|
|
219
|
+
if path.is_symlink() or not path.is_file():
|
|
220
|
+
raise CorruptArtifactError(f"Artifact path is not a regular file: {path}")
|
|
221
|
+
try:
|
|
222
|
+
encoded = path.read_bytes()
|
|
223
|
+
except OSError as exc:
|
|
224
|
+
raise CorruptArtifactError(f"Artifact file cannot be read: {path}") from exc
|
|
225
|
+
return self._decode_container(
|
|
226
|
+
encoded,
|
|
227
|
+
path=path,
|
|
228
|
+
expected_artifact_id=expected_artifact_id,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def _verified_unlocked(self, artifact_id: ArtifactId) -> tuple[ArtifactRef, bytes]:
|
|
232
|
+
require_artifact_id(artifact_id)
|
|
233
|
+
path = self.path_for_artifact(artifact_id)
|
|
234
|
+
if not path.exists() and not path.is_symlink():
|
|
235
|
+
raise ArtifactNotFoundError(f"Artifact {artifact_id} was not found")
|
|
236
|
+
return self._read_path_unlocked(path, expected_artifact_id=artifact_id)
|
|
237
|
+
|
|
238
|
+
def _verify_all_unlocked(self) -> None:
|
|
239
|
+
for path in sorted(self.root.glob(f"*{_FILE_SUFFIX}")):
|
|
240
|
+
serialized_id = path.name[: -len(_FILE_SUFFIX)]
|
|
241
|
+
try:
|
|
242
|
+
artifact_id = ArtifactId(serialized_id)
|
|
243
|
+
except (TypeError, ValueError) as exc:
|
|
244
|
+
raise CorruptArtifactError(
|
|
245
|
+
f"Artifact file has an invalid content-addressed name: {path}"
|
|
246
|
+
) from exc
|
|
247
|
+
self._read_path_unlocked(path, expected_artifact_id=artifact_id)
|
|
248
|
+
|
|
249
|
+
@staticmethod
|
|
250
|
+
def _write_all(fd: int, content: bytes) -> None:
|
|
251
|
+
view = memoryview(content)
|
|
252
|
+
while view:
|
|
253
|
+
written = os.write(fd, view)
|
|
254
|
+
if written <= 0: # pragma: no cover - defensive OS failure guard.
|
|
255
|
+
raise OSError("zero-byte write while persisting artifact")
|
|
256
|
+
view = view[written:]
|
|
257
|
+
|
|
258
|
+
@staticmethod
|
|
259
|
+
def _fsync_directory_path(path: Path) -> None:
|
|
260
|
+
if os.name != "posix": # pragma: no cover - directory fsync is POSIX-only.
|
|
261
|
+
return
|
|
262
|
+
flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0)
|
|
263
|
+
fd = os.open(path, flags)
|
|
264
|
+
try:
|
|
265
|
+
os.fsync(fd)
|
|
266
|
+
finally:
|
|
267
|
+
os.close(fd)
|
|
268
|
+
|
|
269
|
+
def _fsync_root_directory(self) -> None:
|
|
270
|
+
self._fsync_directory_path(self.root)
|
|
271
|
+
|
|
272
|
+
def _atomic_write_unlocked(self, path: Path, encoded: bytes) -> None:
|
|
273
|
+
fd, temporary_name = tempfile.mkstemp(
|
|
274
|
+
dir=self.root,
|
|
275
|
+
prefix=".artifact-write-",
|
|
276
|
+
suffix=".tmp",
|
|
277
|
+
)
|
|
278
|
+
temporary_path = Path(temporary_name)
|
|
279
|
+
try:
|
|
280
|
+
self._write_all(fd, encoded)
|
|
281
|
+
os.fsync(fd)
|
|
282
|
+
os.close(fd)
|
|
283
|
+
fd = -1
|
|
284
|
+
os.replace(temporary_path, path)
|
|
285
|
+
self._fsync_root_directory()
|
|
286
|
+
finally:
|
|
287
|
+
if fd >= 0:
|
|
288
|
+
os.close(fd)
|
|
289
|
+
try:
|
|
290
|
+
temporary_path.unlink()
|
|
291
|
+
except FileNotFoundError:
|
|
292
|
+
pass
|
|
293
|
+
|
|
294
|
+
def put_bytes(self, content: bytes, *, media_type: str) -> ArtifactRef:
|
|
295
|
+
require_content_and_media_type(content, media_type)
|
|
296
|
+
ref = artifact_domain.artifact_ref_for_bytes(content, media_type=media_type)
|
|
297
|
+
path = self.path_for_artifact(ref.artifact_id)
|
|
298
|
+
with self._locked():
|
|
299
|
+
if path.exists() or path.is_symlink():
|
|
300
|
+
existing_ref, existing_content = self._read_path_unlocked(
|
|
301
|
+
path,
|
|
302
|
+
expected_artifact_id=ref.artifact_id,
|
|
303
|
+
)
|
|
304
|
+
if existing_content != content:
|
|
305
|
+
raise ArtifactCollisionError(
|
|
306
|
+
f"Different payload bytes resolved to {ref.artifact_id}"
|
|
307
|
+
)
|
|
308
|
+
if existing_ref.media_type != media_type:
|
|
309
|
+
raise ArtifactCollisionError(
|
|
310
|
+
f"Different media types resolved to {ref.artifact_id}"
|
|
311
|
+
)
|
|
312
|
+
# A previous attempt may have completed its atomic rename but
|
|
313
|
+
# reported failure while syncing the directory. Re-syncing here
|
|
314
|
+
# makes an idempotent retry a durability-recovery operation.
|
|
315
|
+
self._fsync_root_directory()
|
|
316
|
+
return existing_ref
|
|
317
|
+
|
|
318
|
+
self._atomic_write_unlocked(path, self._encode_container(ref, content))
|
|
319
|
+
persisted_ref, persisted_content = self._read_path_unlocked(
|
|
320
|
+
path,
|
|
321
|
+
expected_artifact_id=ref.artifact_id,
|
|
322
|
+
)
|
|
323
|
+
if persisted_ref != ref or persisted_content != content:
|
|
324
|
+
raise CorruptArtifactError(
|
|
325
|
+
f"Artifact {ref.artifact_id} did not verify after persistence"
|
|
326
|
+
)
|
|
327
|
+
return persisted_ref
|
|
328
|
+
|
|
329
|
+
def stat(self, artifact_id: ArtifactId) -> ArtifactRef:
|
|
330
|
+
with self._locked():
|
|
331
|
+
ref, _ = self._verified_unlocked(artifact_id)
|
|
332
|
+
return ref
|
|
333
|
+
|
|
334
|
+
def read_bytes(
|
|
335
|
+
self,
|
|
336
|
+
artifact_id: ArtifactId,
|
|
337
|
+
*,
|
|
338
|
+
expected_media_type: str | None = None,
|
|
339
|
+
) -> bytes:
|
|
340
|
+
with self._locked():
|
|
341
|
+
ref, content = self._verified_unlocked(artifact_id)
|
|
342
|
+
verify_expected_media_type(ref, expected_media_type)
|
|
343
|
+
return content
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Thread-safe in-memory content-addressed artifact store."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
import agent_evolve.domain.artifact as artifact_domain
|
|
8
|
+
from agent_evolve.domain.artifact import ArtifactRef
|
|
9
|
+
from agent_evolve.domain.ids import ArtifactId
|
|
10
|
+
from agent_evolve.infrastructure.artifacts._verification import (
|
|
11
|
+
require_artifact_id,
|
|
12
|
+
require_content_and_media_type,
|
|
13
|
+
verify_content,
|
|
14
|
+
verify_expected_media_type,
|
|
15
|
+
)
|
|
16
|
+
from agent_evolve.ports.artifact_store import (
|
|
17
|
+
ArtifactCollisionError,
|
|
18
|
+
ArtifactNotFoundError,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InMemoryArtifactStore:
|
|
23
|
+
"""Verified ephemeral store used by tests and non-durable runs."""
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
self._artifacts: dict[ArtifactId, tuple[ArtifactRef, bytes]] = {}
|
|
27
|
+
self._lock = threading.RLock()
|
|
28
|
+
|
|
29
|
+
def put_bytes(self, content: bytes, *, media_type: str) -> ArtifactRef:
|
|
30
|
+
require_content_and_media_type(content, media_type)
|
|
31
|
+
ref = artifact_domain.artifact_ref_for_bytes(content, media_type=media_type)
|
|
32
|
+
with self._lock:
|
|
33
|
+
existing = self._artifacts.get(ref.artifact_id)
|
|
34
|
+
if existing is None:
|
|
35
|
+
self._artifacts[ref.artifact_id] = (ref, content)
|
|
36
|
+
return ref
|
|
37
|
+
|
|
38
|
+
existing_ref, existing_content = existing
|
|
39
|
+
verify_content(existing_ref, existing_content)
|
|
40
|
+
if existing_content != content:
|
|
41
|
+
raise ArtifactCollisionError(
|
|
42
|
+
f"Different payload bytes resolved to {ref.artifact_id}"
|
|
43
|
+
)
|
|
44
|
+
if existing_ref.media_type != media_type:
|
|
45
|
+
raise ArtifactCollisionError(
|
|
46
|
+
f"Different media types resolved to {ref.artifact_id}"
|
|
47
|
+
)
|
|
48
|
+
return existing_ref
|
|
49
|
+
|
|
50
|
+
def _verified(self, artifact_id: ArtifactId) -> tuple[ArtifactRef, bytes]:
|
|
51
|
+
require_artifact_id(artifact_id)
|
|
52
|
+
existing = self._artifacts.get(artifact_id)
|
|
53
|
+
if existing is None:
|
|
54
|
+
raise ArtifactNotFoundError(f"Artifact {artifact_id} was not found")
|
|
55
|
+
ref, content = existing
|
|
56
|
+
verify_content(ref, content)
|
|
57
|
+
return ref, content
|
|
58
|
+
|
|
59
|
+
def stat(self, artifact_id: ArtifactId) -> ArtifactRef:
|
|
60
|
+
with self._lock:
|
|
61
|
+
ref, _ = self._verified(artifact_id)
|
|
62
|
+
return ref
|
|
63
|
+
|
|
64
|
+
def read_bytes(
|
|
65
|
+
self,
|
|
66
|
+
artifact_id: ArtifactId,
|
|
67
|
+
*,
|
|
68
|
+
expected_media_type: str | None = None,
|
|
69
|
+
) -> bytes:
|
|
70
|
+
with self._lock:
|
|
71
|
+
ref, content = self._verified(artifact_id)
|
|
72
|
+
verify_expected_media_type(ref, expected_media_type)
|
|
73
|
+
return content
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Asyncio timing adapter for the provider-neutral LLM task queue."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections.abc import Awaitable, Callable
|
|
7
|
+
from contextlib import suppress
|
|
8
|
+
from typing import TypeVar
|
|
9
|
+
|
|
10
|
+
from agent_evolve.domain.llm_task_queue import NANOSECONDS_PER_SECOND
|
|
11
|
+
from agent_evolve.ports.llm_task_queue import ExecutorRetiredError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
AwaitedT = TypeVar("AwaitedT")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TransportAbortedTimeoutError(TimeoutError, ExecutorRetiredError):
|
|
18
|
+
"""The deadline expired and the owned transport was retired cleanly.
|
|
19
|
+
|
|
20
|
+
Unlike a plain ``TimeoutError``, this condition cannot be retried on the
|
|
21
|
+
same provider executor: its transport has deliberately been closed to
|
|
22
|
+
guarantee that the timed-out request is no longer live.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AsyncioRuntime:
|
|
27
|
+
"""Own real sleeps and timeout containment outside provider adapters.
|
|
28
|
+
|
|
29
|
+
``asyncio.wait_for`` is not a hard wall-clock bound: after its deadline it
|
|
30
|
+
cancels the child and waits indefinitely for cancellation cleanup. A
|
|
31
|
+
provider stack may legitimately shield transport cleanup, which leaves the
|
|
32
|
+
queue unable to publish a timeout. When an owned ``timeout_abort`` is
|
|
33
|
+
supplied, this runtime instead observes the deadline, cancels the attempt,
|
|
34
|
+
closes its transport, and drains the attempt before returning a terminal
|
|
35
|
+
:class:`TransportAbortedTimeoutError`.
|
|
36
|
+
|
|
37
|
+
The abort callback must be idempotent and must retire every external
|
|
38
|
+
operation reachable from the awaitable. Production composition binds it
|
|
39
|
+
to the owned provider client's asynchronous close operation.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
*,
|
|
45
|
+
timeout_abort: Callable[[], Awaitable[None]] | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
if timeout_abort is not None and not callable(timeout_abort):
|
|
48
|
+
raise TypeError("timeout_abort must be callable or None")
|
|
49
|
+
self._timeout_abort = timeout_abort
|
|
50
|
+
|
|
51
|
+
async def sleep(self, delay_ns: int) -> None:
|
|
52
|
+
if type(delay_ns) is not int or delay_ns < 0:
|
|
53
|
+
raise ValueError("delay_ns must be a non-negative integer")
|
|
54
|
+
await asyncio.sleep(delay_ns / NANOSECONDS_PER_SECOND)
|
|
55
|
+
|
|
56
|
+
async def wait_for(
|
|
57
|
+
self,
|
|
58
|
+
awaitable: Awaitable[AwaitedT],
|
|
59
|
+
timeout_ns: int | None,
|
|
60
|
+
) -> AwaitedT:
|
|
61
|
+
if timeout_ns is not None and (
|
|
62
|
+
type(timeout_ns) is not int or timeout_ns <= 0
|
|
63
|
+
):
|
|
64
|
+
raise ValueError("timeout_ns must be a positive integer or None")
|
|
65
|
+
if self._timeout_abort is None:
|
|
66
|
+
if timeout_ns is None:
|
|
67
|
+
return await awaitable
|
|
68
|
+
return await asyncio.wait_for(
|
|
69
|
+
awaitable,
|
|
70
|
+
timeout=timeout_ns / NANOSECONDS_PER_SECOND,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
attempt = asyncio.ensure_future(awaitable)
|
|
74
|
+
try:
|
|
75
|
+
if timeout_ns is None:
|
|
76
|
+
return await attempt
|
|
77
|
+
done, _ = await asyncio.wait(
|
|
78
|
+
(attempt,),
|
|
79
|
+
timeout=timeout_ns / NANOSECONDS_PER_SECOND,
|
|
80
|
+
return_when=asyncio.ALL_COMPLETED,
|
|
81
|
+
)
|
|
82
|
+
except asyncio.CancelledError:
|
|
83
|
+
await self._abort_and_drain(attempt)
|
|
84
|
+
raise
|
|
85
|
+
|
|
86
|
+
if done:
|
|
87
|
+
return attempt.result()
|
|
88
|
+
|
|
89
|
+
await self._abort_and_drain(attempt)
|
|
90
|
+
raise TransportAbortedTimeoutError from None
|
|
91
|
+
|
|
92
|
+
async def _abort_and_drain(self, attempt: "asyncio.Future[object]") -> None:
|
|
93
|
+
"""Retire external I/O and leave no child attempt behind."""
|
|
94
|
+
|
|
95
|
+
attempt.cancel()
|
|
96
|
+
abort_error: BaseException | None = None
|
|
97
|
+
try:
|
|
98
|
+
assert self._timeout_abort is not None
|
|
99
|
+
await self._timeout_abort()
|
|
100
|
+
except BaseException as error: # retain cleanup, never provider text
|
|
101
|
+
abort_error = error
|
|
102
|
+
|
|
103
|
+
with suppress(BaseException):
|
|
104
|
+
await attempt
|
|
105
|
+
|
|
106
|
+
if not attempt.done(): # defensive: awaited futures must be terminal
|
|
107
|
+
raise RuntimeError("timed-out attempt did not reach a terminal state")
|
|
108
|
+
if abort_error is not None:
|
|
109
|
+
raise RuntimeError("timed-out attempt transport abort failed") from None
|