fix(credits): suppress usage gauge when top-up funds exist + add display.credits_notices toggle (#44716)
The subscription-cap usage gauge (50/75/90% bands) ignored purchased (top-up) credits: a sub user with top-up funds got a sticky warn banner at 90% of their cap — permanently at >=100%, alongside grant_spent — despite being fully able to keep inferencing. The cap is the wrong denominator for an account that can keep spending. - evaluate_credits_notices: purchased_micros > 0 suppresses the usage band (grant_spent already covers the cap-reached + top-up case with the remaining balance). A top-up landing mid-session clears any showing band; spending top-up down to 0 resumes the gauge. - New display.credits_notices config (default true): false silences all credits notices. State capture and /usage are unaffected. Read once per agent (cached) in _emit_credits_notices, fail-open true. - Docs: configuration.md display block.
This commit is contained in:
@@ -2827,6 +2827,8 @@ class AIAgent:
|
||||
"""
|
||||
if getattr(self, "notice_callback", None) is None and getattr(self, "notice_clear_callback", None) is None:
|
||||
return
|
||||
if not self._credits_notices_enabled():
|
||||
return
|
||||
state = getattr(self, "_credits_state", None)
|
||||
if state is None:
|
||||
return
|
||||
@@ -2850,6 +2852,29 @@ class AIAgent:
|
||||
except Exception:
|
||||
logger.warning("credits notice evaluation/emit failed", exc_info=True)
|
||||
|
||||
def _credits_notices_enabled(self) -> bool:
|
||||
"""Whether credits notices are enabled (config display.credits_notices).
|
||||
|
||||
Read once per agent and cached — the policy runs after every API
|
||||
response, and the setting governs UI noise, not correctness, so a
|
||||
config flip applying on the next session is fine. Fail-open True
|
||||
(preserve current behaviour) on any config error.
|
||||
"""
|
||||
cached = getattr(self, "_credits_notices_enabled_cache", None)
|
||||
if cached is not None:
|
||||
return cached
|
||||
enabled = True
|
||||
try:
|
||||
from hermes_cli.config import load_config as _load_config
|
||||
_cfg = _load_config() or {}
|
||||
_display = _cfg.get("display") if isinstance(_cfg, dict) else None
|
||||
if isinstance(_display, dict) and "credits_notices" in _display:
|
||||
enabled = bool(_display.get("credits_notices"))
|
||||
except Exception:
|
||||
enabled = True
|
||||
self._credits_notices_enabled_cache = enabled
|
||||
return enabled
|
||||
|
||||
def get_credits_state(self):
|
||||
"""Return the last captured CreditsState, or None."""
|
||||
return self._credits_state
|
||||
|
||||
Reference in New Issue
Block a user