observable-agent / tests /test_llm.py
sukhrobnurali's picture
Harden 429 quota handling and tighten session cap
9447a57 verified
Raw
History Blame Contribute Delete
2.78 kB
from types import SimpleNamespace
import pytest
from agent import llm
def test_retry_call_retries_then_succeeds():
calls = {"n": 0}
def fn():
calls["n"] += 1
if calls["n"] < 3:
raise ValueError("transient")
return "ok"
out = llm.retry_call(fn, is_retryable=lambda e: isinstance(e, ValueError),
max_retries=5, base_delay=0, sleep=lambda s: None)
assert out == "ok"
assert calls["n"] == 3
def test_retry_call_non_retryable_raises_immediately():
calls = {"n": 0}
def fn():
calls["n"] += 1
raise TypeError("nope")
with pytest.raises(TypeError):
llm.retry_call(fn, is_retryable=lambda e: isinstance(e, ValueError),
max_retries=5, base_delay=0, sleep=lambda s: None)
assert calls["n"] == 1
def test_retry_call_exhausts_and_raises_last():
def fn():
raise ValueError("always")
with pytest.raises(ValueError):
llm.retry_call(fn, is_retryable=lambda e: True,
max_retries=3, base_delay=0, sleep=lambda s: None)
def test_is_retryable_code():
# 429 is intentionally NOT retried: retrying wastes scarce free-tier requests and a short
# backoff cannot clear a per-minute window. Only transient server/timeout errors are retried.
assert not llm.is_retryable_code(429)
assert llm.is_retryable_code(408)
assert llm.is_retryable_code(503)
assert not llm.is_retryable_code(400)
assert not llm.is_retryable_code(None)
def _quota_exc(quota_id):
details = {"error": {"details": [
{"@type": "type.googleapis.com/google.rpc.QuotaFailure",
"violations": [{"quotaId": quota_id}]},
]}}
return SimpleNamespace(code=429, details=details)
def test_map_persistent_error_defaults_429_to_daily():
mapped = llm.map_persistent_error(SimpleNamespace(code=429, details=None))
assert isinstance(mapped, llm.QuotaExhaustedError)
assert "tomorrow" in str(mapped).lower()
def test_map_persistent_error_per_minute_says_wait_not_tomorrow():
mapped = llm.map_persistent_error(
_quota_exc("GenerateRequestsPerMinutePerProjectPerModel-FreeTier")
)
assert isinstance(mapped, llm.QuotaExhaustedError)
msg = str(mapped).lower()
assert "minute" in msg and "tomorrow" not in msg
def test_map_persistent_error_per_day_says_tomorrow():
mapped = llm.map_persistent_error(
_quota_exc("GenerateRequestsPerDayPerProjectPerModel-FreeTier")
)
assert "tomorrow" in str(mapped).lower()
def test_map_persistent_error_passes_other_through():
err = SimpleNamespace(code=503)
assert llm.map_persistent_error(err) is err