refactor(agent/credits): inline usd validator, module-level threading import

This commit is contained in:
Teknium
2026-09-02 19:24:50 -07:00
parent f49413fde6
commit 378c6e72e6
+7 -19
View File
@@ -9,6 +9,7 @@ from __future__ import annotations
import logging
import os
import re
import threading
import time
from dataclasses import dataclass
from typing import Any, Mapping, Optional
@@ -26,18 +27,12 @@ _SENTINEL = object() # "parse failed"
def _safe_int(value: Any) -> Any:
"""Exact int (money-safe) or ``_SENTINEL``. ``int()`` directly, NOT ``int(float())``
(precision loss above 2**53 corrupts money); float-shaped strings fail."""
if value is None:
return _SENTINEL
try:
return int(str(value))
return _SENTINEL if value is None else int(str(value))
except (TypeError, ValueError):
return _SENTINEL
def _validate_usd(value: Optional[str]) -> bool:
return value is not None and bool(_USD_RE.match(value))
@dataclass
class CreditsState:
"""Credits state parsed from x-nous-credits-* response headers."""
@@ -259,7 +254,7 @@ def _parse_field(kind: str, raw: Optional[str], default: Any = None) -> Any:
val = _safe_int(raw)
return _SENTINEL if val is _SENTINEL or (kind == "micros" and val < 0) else val
if kind == "usd":
return raw if _validate_usd(raw) else _SENTINEL
return raw if raw is not None and _USD_RE.match(raw) else _SENTINEL
if raw is None:
return default
flag = raw.strip().lower()
@@ -404,8 +399,7 @@ def _hydrate_seed_state(agent, state) -> None:
agent._credits_session_start_micros = state.remaining_micros
latch = getattr(agent, "_credits_latch", None)
if isinstance(latch, dict) and state.used_fraction is not None:
# Prime ONLY seen_below_90 — priming seen_grant_unspent would revive the nag (grant-spent is a steady state).
latch["seen_below_90"] = True
latch["seen_below_90"] = True # ONLY this gate — priming seen_grant_unspent would revive the steady-state nag
if callable(emit := getattr(agent, "_emit_credits_notices", None)):
emit()
@@ -426,24 +420,18 @@ def seed_credits_at_session_start(agent) -> bool:
_hydrate_seed_state(agent, fixture)
return True
# FIRE-AND-FORGET: a slow portal must never delay "ready". The daemon
# thread re-checks idempotency (a live header may land first).
import threading
def _bg_seed() -> None:
def _bg_seed() -> None: # FIRE-AND-FORGET: a slow portal must never delay "ready"
try:
from hermes_cli.nous_account import get_nous_portal_account_info
info = get_nous_portal_account_info(force_fresh=True)
if getattr(agent, "_credits_state", None) is not None:
return # a live inference header beat us — don't clobber it
state = _credits_state_from_account(info)
if state is not None:
if (state := _credits_state_from_account(info)) is not None:
_hydrate_seed_state(agent, state)
except Exception:
logger.debug("credits ▸ session-start seed (background) failed", exc_info=True)
threading.Thread(target=_bg_seed, name="credits-seed", daemon=True).start()
return True
except Exception:
# Innermost log across all call sites so a dead seed is diagnosable.
logger.debug("credits ▸ session-start seed failed (fail-open)", exc_info=True)
logger.debug("credits ▸ session-start seed failed (fail-open)", exc_info=True) # innermost log: diagnosable dead seed
return False