mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
Ports agent/account_usage.py and its tests from the original PR #2486 branch. Defines AccountUsageSnapshot / AccountUsageWindow dataclasses, a shared renderer, and provider-specific fetchers for OpenAI Codex (wham/usage), Anthropic OAuth (oauth/usage), and OpenRouter (/credits and /key). Wiring into /usage lands in a follow-up salvage commit. Authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
203 lines
6.5 KiB
Python
203 lines
6.5 KiB
Python
from datetime import datetime, timezone
|
|
|
|
from agent.account_usage import (
|
|
AccountUsageSnapshot,
|
|
AccountUsageWindow,
|
|
fetch_account_usage,
|
|
render_account_usage_lines,
|
|
)
|
|
|
|
|
|
class _Response:
|
|
def __init__(self, payload, status_code=200):
|
|
self._payload = payload
|
|
self.status_code = status_code
|
|
|
|
def raise_for_status(self):
|
|
if self.status_code >= 400:
|
|
raise RuntimeError(f"HTTP {self.status_code}")
|
|
|
|
def json(self):
|
|
return self._payload
|
|
|
|
|
|
class _Client:
|
|
def __init__(self, payload):
|
|
self._payload = payload
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc, tb):
|
|
return False
|
|
|
|
def get(self, url, headers=None):
|
|
return _Response(self._payload)
|
|
|
|
|
|
class _RoutingClient:
|
|
def __init__(self, payloads):
|
|
self._payloads = payloads
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc, tb):
|
|
return False
|
|
|
|
def get(self, url, headers=None):
|
|
return _Response(self._payloads[url])
|
|
|
|
|
|
def test_fetch_account_usage_codex(monkeypatch):
|
|
monkeypatch.setattr(
|
|
"agent.account_usage.resolve_codex_runtime_credentials",
|
|
lambda refresh_if_expiring=True: {
|
|
"provider": "openai-codex",
|
|
"base_url": "https://chatgpt.com/backend-api/codex",
|
|
"api_key": "access-token",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
"agent.account_usage._read_codex_tokens",
|
|
lambda: {"tokens": {"account_id": "acct_123"}},
|
|
)
|
|
monkeypatch.setattr(
|
|
"agent.account_usage.httpx.Client",
|
|
lambda timeout=15.0: _Client(
|
|
{
|
|
"plan_type": "pro",
|
|
"rate_limit": {
|
|
"primary_window": {
|
|
"used_percent": 15,
|
|
"reset_at": 1_900_000_000,
|
|
"limit_window_seconds": 18000,
|
|
},
|
|
"secondary_window": {
|
|
"used_percent": 40,
|
|
"reset_at": 1_900_500_000,
|
|
"limit_window_seconds": 604800,
|
|
},
|
|
},
|
|
"credits": {"has_credits": True, "balance": 12.5},
|
|
}
|
|
),
|
|
)
|
|
|
|
snapshot = fetch_account_usage("openai-codex")
|
|
|
|
assert snapshot is not None
|
|
assert snapshot.plan == "Pro"
|
|
assert len(snapshot.windows) == 2
|
|
assert snapshot.windows[0].label == "Session"
|
|
assert snapshot.windows[0].used_percent == 15.0
|
|
assert snapshot.windows[0].reset_at == datetime.fromtimestamp(1_900_000_000, tz=timezone.utc)
|
|
assert "Credits balance: $12.50" in snapshot.details
|
|
|
|
|
|
def test_render_account_usage_lines_includes_reset_and_provider():
|
|
snapshot = AccountUsageSnapshot(
|
|
provider="openai-codex",
|
|
source="usage_api",
|
|
fetched_at=datetime.now(timezone.utc),
|
|
plan="Pro",
|
|
windows=(
|
|
AccountUsageWindow(
|
|
label="Session",
|
|
used_percent=25,
|
|
reset_at=datetime.now(timezone.utc),
|
|
),
|
|
),
|
|
details=("Credits balance: $9.99",),
|
|
)
|
|
lines = render_account_usage_lines(snapshot)
|
|
|
|
assert lines[0] == "📈 Account limits"
|
|
assert "openai-codex (Pro)" in lines[1]
|
|
assert "Session: 75% remaining (25% used)" in lines[2]
|
|
assert "Credits balance: $9.99" in lines[3]
|
|
|
|
|
|
def test_fetch_account_usage_openrouter_uses_limit_remaining_and_ignores_deprecated_rate_limit(monkeypatch):
|
|
monkeypatch.setattr(
|
|
"agent.account_usage.resolve_runtime_provider",
|
|
lambda requested, explicit_base_url=None, explicit_api_key=None: {
|
|
"provider": "openrouter",
|
|
"base_url": "https://openrouter.ai/api/v1",
|
|
"api_key": "sk-test",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
"agent.account_usage.httpx.Client",
|
|
lambda timeout=10.0: _RoutingClient(
|
|
{
|
|
"https://openrouter.ai/api/v1/credits": {
|
|
"data": {"total_credits": 300.0, "total_usage": 10.92}
|
|
},
|
|
"https://openrouter.ai/api/v1/key": {
|
|
"data": {
|
|
"limit": 100.0,
|
|
"limit_remaining": 70.0,
|
|
"limit_reset": "monthly",
|
|
"usage": 12.5,
|
|
"usage_daily": 0.5,
|
|
"usage_weekly": 2.0,
|
|
"usage_monthly": 8.0,
|
|
"rate_limit": {"requests": -1, "interval": "10s"},
|
|
}
|
|
},
|
|
}
|
|
),
|
|
)
|
|
|
|
snapshot = fetch_account_usage("openrouter")
|
|
|
|
assert snapshot is not None
|
|
assert snapshot.windows == (
|
|
AccountUsageWindow(
|
|
label="API key quota",
|
|
used_percent=30.0,
|
|
detail="$70.00 of $100.00 remaining • resets monthly",
|
|
),
|
|
)
|
|
assert "Credits balance: $289.08" in snapshot.details
|
|
assert "API key usage: $12.50 total • $0.50 today • $2.00 this week • $8.00 this month" in snapshot.details
|
|
assert all("-1 requests / 10s" not in line for line in render_account_usage_lines(snapshot))
|
|
|
|
|
|
def test_fetch_account_usage_openrouter_omits_quota_window_when_key_has_no_limit(monkeypatch):
|
|
monkeypatch.setattr(
|
|
"agent.account_usage.resolve_runtime_provider",
|
|
lambda requested, explicit_base_url=None, explicit_api_key=None: {
|
|
"provider": "openrouter",
|
|
"base_url": "https://openrouter.ai/api/v1",
|
|
"api_key": "sk-test",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
"agent.account_usage.httpx.Client",
|
|
lambda timeout=10.0: _RoutingClient(
|
|
{
|
|
"https://openrouter.ai/api/v1/credits": {
|
|
"data": {"total_credits": 100.0, "total_usage": 25.5}
|
|
},
|
|
"https://openrouter.ai/api/v1/key": {
|
|
"data": {
|
|
"limit": None,
|
|
"limit_remaining": None,
|
|
"usage": 25.5,
|
|
"usage_daily": 1.25,
|
|
"usage_weekly": 4.5,
|
|
"usage_monthly": 18.0,
|
|
}
|
|
},
|
|
}
|
|
),
|
|
)
|
|
|
|
snapshot = fetch_account_usage("openrouter")
|
|
|
|
assert snapshot is not None
|
|
assert snapshot.windows == ()
|
|
assert "Credits balance: $74.50" in snapshot.details
|
|
assert "API key usage: $25.50 total • $1.25 today • $4.50 this week • $18.00 this month" in snapshot.details
|