feat: implement structured exception classes with error codes and sug… (#3279)

This commit is contained in:
Brinlee Kidd
2025-09-19 02:31:35 +05:30
committed by GitHub
parent ac72eb5ecc
commit d4e98dba38
5 changed files with 904 additions and 65 deletions
+181 -30
View File
@@ -9,6 +9,7 @@ import requests
from mem0.client.project import AsyncProject, Project
from mem0.client.utils import api_error_handler
# Exception classes are referenced in docstrings only
from mem0.memory.setup import get_user_id, setup_config
from mem0.memory.telemetry import capture_client_event
@@ -139,7 +140,12 @@ class MemoryClient:
A dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
kwargs = self._prepare_params(kwargs)
if kwargs.get("output_format") != "v1.1":
@@ -173,7 +179,12 @@ class MemoryClient:
A dictionary containing the memory data.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params()
response = self.client.get(f"/v1/memories/{memory_id}/", params=params)
@@ -194,7 +205,12 @@ class MemoryClient:
A list of dictionaries containing memories.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params(kwargs)
if version == "v1":
@@ -236,7 +252,12 @@ class MemoryClient:
A list of dictionaries containing search results.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
payload = {"query": query}
params = self._prepare_params(kwargs)
@@ -303,7 +324,12 @@ class MemoryClient:
A dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params()
response = self.client.delete(f"/v1/memories/{memory_id}/", params=params)
@@ -323,7 +349,12 @@ class MemoryClient:
A dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params(kwargs)
response = self.client.delete("/v1/memories/", params=params)
@@ -346,7 +377,12 @@ class MemoryClient:
A list of dictionaries containing the memory history.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params()
response = self.client.get(f"/v1/memories/{memory_id}/history/", params=params)
@@ -384,7 +420,10 @@ class MemoryClient:
Raises:
ValueError: If specified entity not found
APIError: If deletion fails
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
MemoryNotFoundError: If the entity doesn't exist.
NetworkError: If network connectivity issues occur.
"""
if user_id:
@@ -438,10 +477,13 @@ class MemoryClient:
Dict[str, str]: Message client reset successful.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
# Delete all users, agents, and sessions
# This will also delete the memories
self.delete_users()
capture_client_event("client.reset", self, {"sync_type": "sync"})
@@ -459,6 +501,14 @@ class MemoryClient:
Returns:
Dict[str, Any]: The response from the server.
Raises:
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
response = self.client.put("/v1/batch/", json={"memories": memories})
response.raise_for_status()
@@ -479,7 +529,12 @@ class MemoryClient:
str: Message indicating the success of the batch deletion.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
response = self.client.request("DELETE", "/v1/batch/", json={"memories": memories})
response.raise_for_status()
@@ -560,7 +615,12 @@ class MemoryClient:
Dictionary containing the requested fields.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If org_id or project_id are not set.
"""
logger.warning(
@@ -604,7 +664,12 @@ class MemoryClient:
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If org_id or project_id are not set.
"""
logger.warning(
@@ -673,7 +738,12 @@ class MemoryClient:
Dictionary containing webhook details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If project_id is not set.
"""
@@ -695,7 +765,12 @@ class MemoryClient:
Dictionary containing the created webhook details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If project_id is not set.
"""
@@ -725,7 +800,12 @@ class MemoryClient:
Dictionary containing the updated webhook details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
payload = {k: v for k, v in {"name": name, "url": url, "event_types": event_types}.items() if v is not None}
@@ -745,7 +825,12 @@ class MemoryClient:
Dictionary containing success message.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
response = self.client.delete(f"api/v1/webhooks/{webhook_id}/")
@@ -1098,7 +1183,12 @@ class AsyncMemoryClient:
A dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params()
response = await self.async_client.delete(f"/v1/memories/{memory_id}/", params=params)
@@ -1117,7 +1207,12 @@ class AsyncMemoryClient:
A dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params(kwargs)
response = await self.async_client.delete("/v1/memories/", params=params)
@@ -1136,7 +1231,12 @@ class AsyncMemoryClient:
A list of dictionaries containing the memory history.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
params = self._prepare_params()
response = await self.async_client.get(f"/v1/memories/{memory_id}/history/", params=params)
@@ -1174,7 +1274,10 @@ class AsyncMemoryClient:
Raises:
ValueError: If specified entity not found
APIError: If deletion fails
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
MemoryNotFoundError: If the entity doesn't exist.
NetworkError: If network connectivity issues occur.
"""
if user_id:
@@ -1228,7 +1331,12 @@ class AsyncMemoryClient:
Dict[str, str]: Message client reset successful.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
await self.delete_users()
capture_client_event("client.reset", self, {"sync_type": "async"})
@@ -1246,6 +1354,14 @@ class AsyncMemoryClient:
Returns:
Dict[str, Any]: The response from the server.
Raises:
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
response = await self.async_client.put("/v1/batch/", json={"memories": memories})
response.raise_for_status()
@@ -1266,7 +1382,12 @@ class AsyncMemoryClient:
str: Message indicating the success of the batch deletion.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
response = await self.async_client.request("DELETE", "/v1/batch/", json={"memories": memories})
response.raise_for_status()
@@ -1334,7 +1455,12 @@ class AsyncMemoryClient:
Dictionary containing the requested fields.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If org_id or project_id are not set.
"""
logger.warning(
@@ -1374,7 +1500,12 @@ class AsyncMemoryClient:
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If org_id or project_id are not set.
"""
logger.warning(
@@ -1441,7 +1572,12 @@ class AsyncMemoryClient:
Dictionary containing webhook details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If project_id is not set.
"""
@@ -1463,7 +1599,12 @@ class AsyncMemoryClient:
Dictionary containing the created webhook details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
ValueError: If project_id is not set.
"""
@@ -1493,7 +1634,12 @@ class AsyncMemoryClient:
Dictionary containing the updated webhook details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
payload = {k: v for k, v in {"name": name, "url": url, "event_types": event_types}.items() if v is not None}
@@ -1513,7 +1659,12 @@ class AsyncMemoryClient:
Dictionary containing success message.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
MemoryQuotaExceededError: If memory quota is exceeded.
NetworkError: If network connectivity issues occur.
MemoryNotFoundError: If the memory doesn't exist (for updates/deletes).
"""
response = await self.async_client.delete(f"api/v1/webhooks/{webhook_id}/")
+97 -24
View File
@@ -7,6 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field
from mem0.client.utils import api_error_handler
from mem0.memory.telemetry import capture_client_event
# Exception classes are referenced in docstrings only
logger = logging.getLogger(__name__)
@@ -141,7 +142,10 @@ class BaseProject(ABC):
Dictionary containing the requested project fields.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
pass
@@ -159,7 +163,10 @@ class BaseProject(ABC):
Dictionary containing the created project details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id is not set.
"""
pass
@@ -185,7 +192,10 @@ class BaseProject(ABC):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
pass
@@ -199,7 +209,10 @@ class BaseProject(ABC):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
pass
@@ -213,7 +226,10 @@ class BaseProject(ABC):
Dictionary containing the list of project members.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
pass
@@ -231,7 +247,10 @@ class BaseProject(ABC):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
pass
@@ -249,7 +268,10 @@ class BaseProject(ABC):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
pass
@@ -266,7 +288,10 @@ class BaseProject(ABC):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
pass
@@ -310,7 +335,10 @@ class Project(BaseProject):
Dictionary containing the requested project fields.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
params = self._prepare_params({"fields": fields})
@@ -339,7 +367,10 @@ class Project(BaseProject):
Dictionary containing the created project details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id is not set.
"""
if not self.config.org_id:
@@ -382,7 +413,10 @@ class Project(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
if (
@@ -432,7 +466,10 @@ class Project(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
response = self._client.delete(
@@ -455,7 +492,10 @@ class Project(BaseProject):
Dictionary containing the list of project members.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
response = self._client.get(
@@ -482,7 +522,10 @@ class Project(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
if role not in ["READER", "OWNER"]:
@@ -515,7 +558,10 @@ class Project(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
if role not in ["READER", "OWNER"]:
@@ -547,7 +593,10 @@ class Project(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
params = {"email": email}
@@ -603,7 +652,10 @@ class AsyncProject(BaseProject):
Dictionary containing the requested project fields.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
params = self._prepare_params({"fields": fields})
@@ -632,7 +684,10 @@ class AsyncProject(BaseProject):
Dictionary containing the created project details.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id is not set.
"""
if not self.config.org_id:
@@ -675,7 +730,10 @@ class AsyncProject(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
if (
@@ -725,7 +783,10 @@ class AsyncProject(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
response = await self._client.delete(
@@ -748,7 +809,10 @@ class AsyncProject(BaseProject):
Dictionary containing the list of project members.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
response = await self._client.get(
@@ -775,7 +839,10 @@ class AsyncProject(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
if role not in ["READER", "OWNER"]:
@@ -808,7 +875,10 @@ class AsyncProject(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
if role not in ["READER", "OWNER"]:
@@ -840,7 +910,10 @@ class AsyncProject(BaseProject):
Dictionary containing the API response.
Raises:
APIError: If the API request fails.
ValidationError: If the input data is invalid.
AuthenticationError: If authentication fails.
RateLimitError: If rate limits are exceeded.
NetworkError: If network connectivity issues occur.
ValueError: If org_id or project_id are not set.
"""
params = {"email": email}
+91 -5
View File
@@ -1,18 +1,35 @@
import json
import logging
import httpx
from mem0.exceptions import (
NetworkError,
create_exception_from_response,
)
logger = logging.getLogger(__name__)
class APIError(Exception):
"""Exception raised for errors in the API."""
"""Exception raised for errors in the API.
Deprecated: Use specific exception classes from mem0.exceptions instead.
This class is maintained for backward compatibility.
"""
pass
def api_error_handler(func):
"""Decorator to handle API errors consistently."""
"""Decorator to handle API errors consistently.
This decorator catches HTTP and request errors and converts them to
appropriate structured exception classes with detailed error information.
The decorator analyzes HTTP status codes and response content to create
the most specific exception type with helpful error messages, suggestions,
and debug information.
"""
from functools import wraps
@wraps(func)
@@ -21,9 +38,78 @@ def api_error_handler(func):
return func(*args, **kwargs)
except httpx.HTTPStatusError as e:
logger.error(f"HTTP error occurred: {e}")
raise APIError(f"API request failed: {e.response.text}")
# Extract error details from response
response_text = ""
error_details = {}
debug_info = {
"status_code": e.response.status_code,
"url": str(e.request.url),
"method": e.request.method,
}
try:
response_text = e.response.text
# Try to parse JSON response for additional error details
if e.response.headers.get("content-type", "").startswith("application/json"):
error_data = json.loads(response_text)
if isinstance(error_data, dict):
error_details = error_data
response_text = error_data.get("detail", response_text)
except (json.JSONDecodeError, AttributeError):
# Fallback to plain text response
pass
# Add rate limit information if available
if e.response.status_code == 429:
retry_after = e.response.headers.get("Retry-After")
if retry_after:
try:
debug_info["retry_after"] = int(retry_after)
except ValueError:
pass
# Add rate limit headers if available
for header in ["X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset"]:
value = e.response.headers.get(header)
if value:
debug_info[header.lower().replace("-", "_")] = value
# Create specific exception based on status code
exception = create_exception_from_response(
status_code=e.response.status_code,
response_text=response_text,
details=error_details,
debug_info=debug_info,
)
raise exception
except httpx.RequestError as e:
logger.error(f"Request error occurred: {e}")
raise APIError(f"Request failed: {str(e)}")
# Determine the appropriate exception type based on error type
if isinstance(e, httpx.TimeoutException):
raise NetworkError(
message=f"Request timed out: {str(e)}",
error_code="NET_TIMEOUT",
suggestion="Please check your internet connection and try again",
debug_info={"error_type": "timeout", "original_error": str(e)},
)
elif isinstance(e, httpx.ConnectError):
raise NetworkError(
message=f"Connection failed: {str(e)}",
error_code="NET_CONNECT",
suggestion="Please check your internet connection and try again",
debug_info={"error_type": "connection", "original_error": str(e)},
)
else:
# Generic network error for other request errors
raise NetworkError(
message=f"Network request failed: {str(e)}",
error_code="NET_GENERIC",
suggestion="Please check your internet connection and try again",
debug_info={"error_type": "request", "original_error": str(e)},
)
return wrapper