Files
Prompt-optimizer/src/prometheus/application/use_cases.py
FullStackDev e2d111ce5b feat: error handling, retry with backoff, and circuit breaker
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>
2026-03-29 12:47:55 +00:00

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,
)