Skip to content

Stopper

stopper

Stopper state model for evolution stop condition decisions.

This module defines the immutable state snapshot passed to stoppers when checking if evolution should terminate.

ATTRIBUTE DESCRIPTION
StopperState

Immutable snapshot of evolution state for stopper decisions.

TYPE: class

Examples:

Creating a stopper state:

from gepa_adk.domain.stopper import StopperState

state = StopperState(
    iteration=5,
    best_score=0.85,
    stagnation_counter=2,
    total_evaluations=50,
    candidates_count=3,
    elapsed_seconds=120.5,
)
Notes

This StopperState snapshot is intentionally minimal. Add fields as needed for specific stopper implementations. The frozen dataclass ensures stoppers cannot accidentally mutate evolution state.

See Also

StopperState dataclass

Immutable snapshot of evolution state for stopper decisions.

Provides stoppers with read-only access to evolution metrics without exposing internal engine state.

ATTRIBUTE DESCRIPTION
iteration

Current iteration number (0-indexed).

TYPE: int

best_score

Best score achieved so far.

TYPE: float

stagnation_counter

Number of iterations without improvement.

TYPE: int

total_evaluations

Total examples evaluated (sum of batch sizes).

TYPE: int

candidates_count

Number of candidates in the frontier.

TYPE: int

elapsed_seconds

Wall-clock time since evolution started.

TYPE: float

latest_score

Score recorded for the most recent iteration, accepted or not. 0.0 for an iteration that produced no proposal. None before the first iteration completes; stoppers that need a current score fall back to best_score then.

TYPE: float | None

Examples:

Creating a state snapshot:

from gepa_adk.domain.stopper import StopperState

state = StopperState(
    iteration=10,
    best_score=0.92,
    stagnation_counter=3,
    total_evaluations=100,
    candidates_count=5,
    elapsed_seconds=300.0,
    latest_score=0.88,
)
print(state.best_score)  # 0.92
print(state.latest_score)  # 0.88
print(state.stagnation_counter)  # 3

Attempting to modify raises an error:

state.iteration = 11  # Raises FrozenInstanceError
Notes

A frozen dataclass, all fields are immutable after creation. Using slots=True for memory efficiency. latest_score is the only field with a default, so it stays last.

Source code in src/gepa_adk/domain/stopper.py
@dataclass(frozen=True, slots=True)
class StopperState:
    """Immutable snapshot of evolution state for stopper decisions.

    Provides stoppers with read-only access to evolution metrics
    without exposing internal engine state.

    Attributes:
        iteration (int): Current iteration number (0-indexed).
        best_score (float): Best score achieved so far.
        stagnation_counter (int): Number of iterations without improvement.
        total_evaluations (int): Total examples evaluated (sum of batch sizes).
        candidates_count (int): Number of candidates in the frontier.
        elapsed_seconds (float): Wall-clock time since evolution started.
        latest_score (float | None): Score recorded for the most recent
            iteration, accepted or not. ``0.0`` for an iteration that produced
            no proposal. ``None`` before the first iteration completes; stoppers
            that need a current score fall back to ``best_score`` then.

    Examples:
        Creating a state snapshot:

        ```python
        from gepa_adk.domain.stopper import StopperState

        state = StopperState(
            iteration=10,
            best_score=0.92,
            stagnation_counter=3,
            total_evaluations=100,
            candidates_count=5,
            elapsed_seconds=300.0,
            latest_score=0.88,
        )
        print(state.best_score)  # 0.92
        print(state.latest_score)  # 0.88
        print(state.stagnation_counter)  # 3
        ```

        Attempting to modify raises an error:

        ```python
        state.iteration = 11  # Raises FrozenInstanceError
        ```

    Notes:
        A frozen dataclass, all fields are immutable after creation.
        Using slots=True for memory efficiency. ``latest_score`` is the only
        field with a default, so it stays last.
    """

    iteration: int
    best_score: float
    stagnation_counter: int
    total_evaluations: int
    candidates_count: int
    elapsed_seconds: float
    latest_score: float | None = None