Add robust error handling to the evolution loop and LLM adapters: - Retry utility with exponential backoff for transient errors (429, 5xx, timeouts) - Per-call error isolation in evaluator and judge adapter - Circuit breaker in EvolutionLoop (trips after N consecutive failures) - CLI flags: --max-retries, --error-strategy (skip|retry|abort) - Config fields: max_retries, retry_delay_base, circuit_breaker_threshold, error_strategy - 16 new unit tests covering all error handling paths Co-Authored-By: Paperclip <noreply@paperclip.ing>
80 lines
2.7 KiB
Python
80 lines
2.7 KiB
Python
"""
|
|
Main use case — high-level orchestration.
|
|
|
|
Entry point for business logic. Coordinates bootstrap → evolution → result.
|
|
Contains no technical logic, only orchestration.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from prometheus.application.bootstrap import SyntheticBootstrap
|
|
from prometheus.application.dto import OptimizationConfig, OptimizationResult
|
|
from prometheus.application.evaluator import PromptEvaluator
|
|
from prometheus.application.evolution import EvolutionLoop
|
|
from prometheus.domain.entities import Prompt
|
|
from prometheus.domain.ports import ProposerPort
|
|
|
|
|
|
class OptimizePromptUseCase:
|
|
"""Single MVP use case.
|
|
|
|
Injects dependencies via constructor (dependency injection).
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
evaluator: PromptEvaluator,
|
|
proposer: ProposerPort,
|
|
bootstrap: SyntheticBootstrap,
|
|
):
|
|
self._evaluator = evaluator
|
|
self._proposer = proposer
|
|
self._bootstrap = bootstrap
|
|
|
|
def execute(self, config: OptimizationConfig) -> OptimizationResult:
|
|
"""Full pipeline:
|
|
1. Bootstrap → generate synthetic inputs
|
|
2. Evolution → optimization loop
|
|
3. Return result
|
|
"""
|
|
# Phase 0: Bootstrap
|
|
synthetic_pool = self._bootstrap.run(
|
|
task_description=config.task_description,
|
|
n_examples=config.n_synthetic_inputs,
|
|
)
|
|
|
|
# Phase 1: Evolution
|
|
loop = EvolutionLoop(
|
|
evaluator=self._evaluator,
|
|
proposer=self._proposer,
|
|
bootstrap=self._bootstrap,
|
|
max_iterations=config.max_iterations,
|
|
minibatch_size=config.minibatch_size,
|
|
perfect_score=config.perfect_score,
|
|
verbose=config.verbose,
|
|
circuit_breaker_threshold=config.circuit_breaker_threshold,
|
|
error_strategy=config.error_strategy,
|
|
)
|
|
seed_prompt = Prompt(text=config.seed_prompt)
|
|
state = loop.run(seed_prompt, synthetic_pool, config.task_description)
|
|
|
|
# Phase 2: Result
|
|
initial_score = (
|
|
state.candidates[0].best_score if state.candidates else 0.0
|
|
)
|
|
final_score = state.best_candidate.best_score if state.best_candidate else 0.0
|
|
|
|
return OptimizationResult(
|
|
optimized_prompt=(
|
|
state.best_candidate.prompt.text
|
|
if state.best_candidate
|
|
else config.seed_prompt
|
|
),
|
|
initial_prompt=config.seed_prompt,
|
|
iterations_used=state.iteration,
|
|
total_llm_calls=state.total_llm_calls + 1, # +1 for bootstrap
|
|
initial_score=initial_score,
|
|
final_score=final_score,
|
|
improvement=final_score - initial_score,
|
|
history=state.history,
|
|
)
|