| """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 | |
| 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})") | |