Self-hosted dashboard and admin auth (#4837)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
This commit is contained in:
co-authored by
kartik-mem0
parent
15feaa8ac4
commit
db8ac61713
@@ -0,0 +1,95 @@
|
||||
import logging
|
||||
import sys
|
||||
import uuid
|
||||
from contextvars import ContextVar
|
||||
|
||||
from fastapi import HTTPException, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
request_id_var: ContextVar[str] = ContextVar("request_id", default="-")
|
||||
|
||||
|
||||
class UpstreamError(HTTPException):
|
||||
def __init__(self, code: str, detail: str, request_id: str) -> None:
|
||||
super().__init__(status_code=502, detail=detail)
|
||||
self.code = code
|
||||
self.request_id = request_id
|
||||
|
||||
|
||||
_AUTH_NAMES = {"AuthenticationError", "PermissionDeniedError"}
|
||||
_RATE_NAMES = {"RateLimitError"}
|
||||
_TIMEOUT_NAMES = {"APITimeoutError"}
|
||||
_CONN_NAMES = {"APIConnectionError", "ConnectionError"}
|
||||
_BAD_REQUEST_NAMES = {"BadRequestError", "UnprocessableEntityError"}
|
||||
_DB_NAMES = {"OperationalError", "DBAPIError", "DisconnectionError"}
|
||||
_VECTOR_NAMES = {"UnexpectedResponse", "ResponseHandlingException"}
|
||||
|
||||
|
||||
def _classify_one(exc: BaseException) -> tuple[str, str]:
|
||||
name = type(exc).__name__
|
||||
module = getattr(type(exc), "__module__", "") or ""
|
||||
status = getattr(exc, "status_code", None)
|
||||
|
||||
if name in _AUTH_NAMES or status in (401, 403):
|
||||
return (
|
||||
"provider_auth_failed",
|
||||
"Provider rejected the request (authentication). "
|
||||
"Check your LLM provider API key on the Configuration page.",
|
||||
)
|
||||
if name in _RATE_NAMES or status == 429:
|
||||
return ("provider_rate_limited", "Provider rate limit hit. Retry shortly.")
|
||||
if name in _TIMEOUT_NAMES or isinstance(exc, TimeoutError):
|
||||
return ("provider_timeout", "Provider timed out. Retry shortly.")
|
||||
if name in _CONN_NAMES or (isinstance(status, int) and status >= 500):
|
||||
return ("provider_unavailable", "Provider is unreachable or returned a server error.")
|
||||
if name in _BAD_REQUEST_NAMES or status in (400, 422):
|
||||
return ("provider_bad_request", "Provider rejected the request as malformed.")
|
||||
if name in _DB_NAMES:
|
||||
return ("datastore_unavailable", "The memory database is unreachable.")
|
||||
if name in _VECTOR_NAMES or module.startswith("qdrant_client"):
|
||||
return ("vector_store_unavailable", "The vector store is unreachable or returned an error.")
|
||||
return ("unknown", "Upstream provider error.")
|
||||
|
||||
|
||||
def _classify(exc: BaseException | None) -> tuple[str, str]:
|
||||
# Walk the cause/context chain so wrapped provider errors still classify correctly.
|
||||
seen: set[int] = set()
|
||||
current = exc
|
||||
while current is not None and id(current) not in seen:
|
||||
seen.add(id(current))
|
||||
result = _classify_one(current)
|
||||
if result[0] != "unknown":
|
||||
return result
|
||||
current = current.__cause__ or current.__context__
|
||||
return ("unknown", "Upstream provider error.")
|
||||
|
||||
|
||||
def new_request_id() -> str:
|
||||
return uuid.uuid4().hex[:8]
|
||||
|
||||
|
||||
def upstream_error() -> UpstreamError:
|
||||
exc = sys.exc_info()[1]
|
||||
code, message = _classify(exc)
|
||||
rid = request_id_var.get()
|
||||
logging.exception("Upstream provider error (code=%s)", code)
|
||||
return UpstreamError(code=code, detail=message, request_id=rid)
|
||||
|
||||
|
||||
async def upstream_error_handler(_: Request, exc: UpstreamError) -> JSONResponse:
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={"detail": exc.detail, "code": exc.code, "request_id": exc.request_id},
|
||||
headers={"X-Request-ID": exc.request_id},
|
||||
)
|
||||
|
||||
|
||||
def install_request_id_logging() -> None:
|
||||
old_factory = logging.getLogRecordFactory()
|
||||
|
||||
def factory(*args, **kwargs):
|
||||
record = old_factory(*args, **kwargs)
|
||||
record.request_id = request_id_var.get()
|
||||
return record
|
||||
|
||||
logging.setLogRecordFactory(factory)
|
||||
Reference in New Issue
Block a user