Source code for ralph.recovery.failure_budget

"""Per-agent failure budget wrapper."""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

from .budget_state import BudgetState

if TYPE_CHECKING:
    from .classifier import ClassifiedFailure


[docs] @dataclass(frozen=True) class FailureBudget: """Per-agent failure budget wrapper.""" state: BudgetState
[docs] def debit(self, failure: ClassifiedFailure) -> FailureBudget: """Return a new budget with the failure counted (only if it counts). The previous ``failures=(*self.state.failures, failure)`` accumulator was removed in wt-024 memory-perf AC-01: the failures tuple was appended on every debit and never read for any decision, while retaining heavyweight ``ClassifiedFailure`` objects (original_exception + traceback frames) for the lifetime of the budget. Only ``consumed`` is needed to drive the exhausted / remaining decisions. """ if not failure.counts_against_budget: return self new_state = BudgetState( max_retries=self.state.max_retries, consumed=self.state.consumed + 1, ) return FailureBudget(state=new_state)
@property def exhausted(self) -> bool: return self.state.exhausted @property def remaining(self) -> int: return self.state.remaining