from __future__ import annotations from typing import Any from fastapi.responses import JSONResponse def _message_from_value(value: object) -> str: if isinstance(value, str): return value if not isinstance(value, dict): return "" message = value.get("message") if isinstance(message, str) and message: return message return _message_from_value(value.get("error")) def error_message_from_detail(detail: object) -> str: if isinstance(detail, list): messages = [] for item in detail: if not isinstance(item, dict): continue location = ".".join(str(part) for part in item.get("loc", []) if part != "body") message = str(item.get("msg") or "").strip() if location and message: messages.append(f"{location}: {message}") elif message: messages.append(message) return "; ".join(messages) if isinstance(detail, dict): message = _message_from_value(detail.get("error")) or _message_from_value(detail) if message: return message return str(detail or "").strip() def _default_error_type(status_code: int) -> str: if status_code == 401: return "authentication_error" if status_code == 403: return "permission_error" if status_code == 429: return "rate_limit_error" if 400 <= status_code < 500: return "invalid_request_error" return "server_error" def _default_error_code(status_code: int) -> str: if status_code == 401: return "invalid_api_key" if status_code == 403: return "permission_denied" if status_code == 429: return "rate_limit_exceeded" if 400 <= status_code < 500: return "bad_request" return "upstream_error" def openai_error_payload( detail: object, status_code: int, *, error_type: str | None = None, code: object | None = None, param: object | None = None, ) -> dict[str, Any]: error_detail = detail.get("error") if isinstance(detail, dict) else None if isinstance(error_detail, dict): return { "error": { "message": error_message_from_detail(error_detail) or "request failed", "type": str(error_detail.get("type") or error_type or _default_error_type(status_code)), "param": error_detail.get("param", param), "code": error_detail.get("code", code if code is not None else _default_error_code(status_code)), } } return { "error": { "message": error_message_from_detail(detail) or "request failed", "type": error_type or _default_error_type(status_code), "param": param, "code": code if code is not None else _default_error_code(status_code), } } def openai_error_response( detail: object, status_code: int, *, headers: dict[str, str] | None = None, error_type: str | None = None, code: object | None = None, param: object | None = None, ) -> JSONResponse: return JSONResponse( status_code=status_code, content=openai_error_payload(detail, status_code, error_type=error_type, code=code, param=param), headers=headers, ) def anthropic_error_response( detail: object, status_code: int, *, headers: dict[str, str] | None = None, ) -> JSONResponse: error_type = "api_error" if status_code >= 500 else _default_error_type(status_code) return JSONResponse( status_code=status_code, content={ "type": "error", "error": { "type": error_type, "message": error_message_from_detail(detail) or "request failed", }, }, headers=headers, )