Spaces:
Sleeping
Sleeping
| 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 | |