mirror of
https://github.com/LearningCircuit/local-deep-research.git
synced 2026-06-15 19:46:56 +03:00
fix(errors): dispatch RateLimitError to RATE_LIMIT_ERROR, not MODEL_ERROR (#4086)
openai.RateLimitError subclasses APIError and was falling through to the openai_unknown catch-all, which maps to MODEL_ERROR. Since MODEL_ERROR is checked before RATE_LIMIT_ERROR in categorize_error, a 429 was never categorised as a rate-limit — users got the wrong error title, wrong suggestions, and missed the API_QUOTA_WARNING notification. Add an explicit RateLimitError branch in _dispatch() that produces an openai_rate_limit token, map it to RATE_LIMIT_ERROR in ErrorReporter, and add a Site C solution branch.
This commit is contained in:
@@ -76,6 +76,8 @@ class ErrorReporter:
|
||||
r"API rate limit",
|
||||
r"maximum.*requests.*minute",
|
||||
r"maximum.*requests.*hour",
|
||||
# OpenAI-compatible endpoint token (#3878 follow-up)
|
||||
r"Error type: openai_rate_limit",
|
||||
],
|
||||
ErrorCategory.SEARCH_ERROR: [
|
||||
r"Search.*failed",
|
||||
|
||||
@@ -130,6 +130,16 @@ def _dispatch(
|
||||
f"model currently loaded in {provider}.",
|
||||
)
|
||||
|
||||
# Rate limit (429) -- must be checked before the APIError catch-all
|
||||
# because RateLimitError subclasses APIStatusError -> APIError.
|
||||
if _is("RateLimitError"):
|
||||
return (
|
||||
"openai_rate_limit",
|
||||
f"{provider} at {base_url} rate-limited the request for model "
|
||||
f"'{model}'. Wait a moment and retry, or enable LLM rate "
|
||||
"limiting in Settings.",
|
||||
)
|
||||
|
||||
# Bad request (400)
|
||||
if _is("BadRequestError"):
|
||||
return (
|
||||
|
||||
@@ -1568,6 +1568,10 @@ def run_research_process(research_id, query, mode, **kwargs):
|
||||
error_context = {
|
||||
"solution": "Check the provider's logs for the full error and verify the base URL / model id."
|
||||
}
|
||||
elif "Error type: openai_rate_limit" in user_friendly_error:
|
||||
error_context = {
|
||||
"solution": "The provider rate-limited the request. Wait a moment and retry, or enable LLM Rate Limiting in Settings."
|
||||
}
|
||||
|
||||
# Generate enhanced error report for failed research
|
||||
enhanced_report_content = None
|
||||
|
||||
@@ -17,6 +17,7 @@ from openai import (
|
||||
BadRequestError,
|
||||
NotFoundError,
|
||||
PermissionDeniedError,
|
||||
RateLimitError,
|
||||
)
|
||||
|
||||
from local_deep_research.error_handling.error_reporter import (
|
||||
@@ -229,6 +230,7 @@ class TestErrorReporterCategorisation:
|
||||
("openai_model_not_found", ErrorCategory.MODEL_ERROR),
|
||||
("openai_bad_request", ErrorCategory.MODEL_ERROR),
|
||||
("openai_unknown", ErrorCategory.MODEL_ERROR),
|
||||
("openai_rate_limit", ErrorCategory.RATE_LIMIT_ERROR),
|
||||
],
|
||||
)
|
||||
def test_token_to_category(
|
||||
@@ -433,3 +435,47 @@ class TestFriendlyErrorNoneArgs:
|
||||
model=None,
|
||||
)
|
||||
assert "<unspecified>" in msg
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# RateLimitError dispatch (follow-up to #3878)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestRateLimitErrorDispatch:
|
||||
"""``openai.RateLimitError`` subclasses ``APIError`` via
|
||||
``APIStatusError``. It must dispatch to ``openai_rate_limit`` (mapped to
|
||||
RATE_LIMIT_ERROR) instead of falling through to the ``openai_unknown``
|
||||
catch-all (mapped to MODEL_ERROR).
|
||||
|
||||
Without the explicit branch, a 429 is mis-categorised as MODEL_ERROR,
|
||||
producing the wrong suggestions and skipping the API_QUOTA_WARNING
|
||||
notification.
|
||||
"""
|
||||
|
||||
def test_rate_limit_dispatches_to_rate_limit_token(self):
|
||||
exc = RateLimitError(
|
||||
message="Rate limit exceeded",
|
||||
response=_resp(429),
|
||||
body=None,
|
||||
)
|
||||
msg = friendly_openai_compatible_error(
|
||||
exc,
|
||||
provider="openrouter",
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
model="gpt-4o",
|
||||
)
|
||||
assert "Error type: openai_rate_limit" in msg
|
||||
assert "Error type: openai_unknown" not in msg
|
||||
assert "rate-limited" in msg
|
||||
|
||||
def test_rate_limit_categorised_as_rate_limit_error(self):
|
||||
reporter = ErrorReporter()
|
||||
message = (
|
||||
"openrouter at https://openrouter.ai/api/v1 rate-limited the "
|
||||
"request for model 'gpt-4o'. (Error type: openai_rate_limit) "
|
||||
"| Details: Error code: 429"
|
||||
)
|
||||
assert (
|
||||
reporter.categorize_error(message) == ErrorCategory.RATE_LIMIT_ERROR
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user