Skip to content

BudgetTracker

gepa.optimize_anything.BudgetTracker(max_evals: int | None = None) dataclass

Thread-safe, in-process eval-call budget enforcer.

Parameters:

Name Type Description Default
max_evals int | None

Maximum number of evaluation calls allowed. None means unlimited eval calls — valid for proposer-cost-only runs, where the run is instead bounded by the engine's max_token_cost cap.

None

Attributes

max_evals: int | None = None class-attribute instance-attribute

used: int property

remaining: int | None property

exhausted: bool property

Methods:

record(score: float) -> None

Record one eval call. Raises BudgetExhausted if over eval limit.

Source code in gepa/oa/budget.py
def record(self, score: float) -> None:
    """Record one eval call. Raises BudgetExhausted if over eval limit."""
    with self._lock:
        if self.max_evals is not None and self._used >= self.max_evals:
            raise BudgetExhausted(f"Eval budget exhausted: {self._used}/{self.max_evals} used")
        self._used += 1
        self._log.append({"eval": self._used, "score": score, "time": time.time()})

check() -> None

Raise BudgetExhausted if the eval budget is used up.

Source code in gepa/oa/budget.py
def check(self) -> None:
    """Raise BudgetExhausted if the eval budget is used up."""
    if self.max_evals is not None and self._used >= self.max_evals:
        raise BudgetExhausted(f"Eval budget exhausted: {self._used}/{self.max_evals} used")

status() -> dict[str, Any]

Source code in gepa/oa/budget.py
def status(self) -> dict[str, Any]:
    result: dict[str, Any] = {"exhausted": self.exhausted}
    if self.max_evals is not None:
        result["max_evals"] = self.max_evals
        result["used"] = self._used
        result["remaining_evals"] = self.remaining
    return result