expAge
feat(phase-11): moteur d'inférence + consolidation + soumission
5c1d1c7
"""Budget d'appels HTTP pour borner le coût d'une inférence.
Chaque appel `relations_from` au graphe JDM consomme une unité. Quand le
plafond est atteint, `BudgetExhausted` est levée — le moteur l'attrape et
renvoie un résultat silencieux plutôt que de laisser filer une exception.
"""
from __future__ import annotations
class BudgetExhausted(RuntimeError):
"""Levée quand le budget d'appels JDM d'une inférence est épuisé."""
class LookupBudget:
"""Compte les appels JDM consommés et coupe net au plafond."""
def __init__(self, limit: int) -> None:
self.limit: int = max(1, int(limit))
self.used: int = 0
@property
def remaining(self) -> int:
return max(0, self.limit - self.used)
def can_afford(self, n: int = 1) -> bool:
return self.used + n <= self.limit
def spend(self, n: int = 1) -> None:
"""Consomme `n` unités ; lève BudgetExhausted si on dépasse."""
self.used += n
if self.used > self.limit:
raise BudgetExhausted(f"budget épuisé ({self.used}/{self.limit})")