From fe0e65928d26a27fa9861904266ed1413e737ef6 Mon Sep 17 00:00:00 2001 From: santhreal <64453045+santhreal@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:20:51 -0700 Subject: [PATCH] fix(utils): coerce Tavily usage counters to int JSON APIs sometimes return numeric fields as strings; subtracting them raised TypeError and /status dropped the usage block. --- nanobot/utils/searchusage.py | 20 +++++++++++++++----- tests/utils/test_searchusage.py | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/nanobot/utils/searchusage.py b/nanobot/utils/searchusage.py index ac490aad..94f76775 100644 --- a/nanobot/utils/searchusage.py +++ b/nanobot/utils/searchusage.py @@ -146,8 +146,8 @@ def _parse_tavily_usage(data: dict[str, Any]) -> SearchUsageInfo: } """ account = data.get("account") or {} - used = account.get("plan_usage") - limit = account.get("plan_limit") + used = _optional_int(account.get("plan_usage")) + limit = _optional_int(account.get("plan_limit")) # Compute remaining remaining = None @@ -160,9 +160,19 @@ def _parse_tavily_usage(data: dict[str, Any]) -> SearchUsageInfo: used=used, limit=limit, remaining=remaining, - search_used=account.get("search_usage"), - extract_used=account.get("extract_usage"), - crawl_used=account.get("crawl_usage"), + search_used=_optional_int(account.get("search_usage")), + extract_used=_optional_int(account.get("extract_usage")), + crawl_used=_optional_int(account.get("crawl_usage")), ) +def _optional_int(value: Any) -> int | None: + """Coerce JSON numerics (including string forms) to int; else None.""" + if value is None: + return None + try: + return int(value) + except (TypeError, ValueError): + return None + + diff --git a/tests/utils/test_searchusage.py b/tests/utils/test_searchusage.py index 10c3a58f..0bb71f5f 100644 --- a/tests/utils/test_searchusage.py +++ b/tests/utils/test_searchusage.py @@ -126,6 +126,24 @@ class TestParseTavilyUsage: assert info.extract_used is None assert info.crawl_used is None + def test_string_numeric_fields(self): + data = { + "account": { + "plan_usage": "10", + "plan_limit": "100", + "search_usage": "7", + "extract_usage": "2", + "crawl_usage": "1", + }, + } + info = _parse_tavily_usage(data) + assert info.used == 10 + assert info.limit == 100 + assert info.remaining == 90 + assert info.search_used == 7 + assert info.extract_used == 2 + assert info.crawl_used == 1 + # --------------------------------------------------------------------------- # fetch_search_usage routing tests