aidial_sdk/exceptions.py (169 lines of code) (raw):
import functools
import warnings
from http import HTTPStatus
from typing import Dict, Optional
from fastapi import HTTPException as FastAPIException
from fastapi.responses import JSONResponse
from aidial_sdk.utils.json import remove_nones
class HTTPException(Exception):
def __init__(
self,
message: str,
status_code: int = 500,
type: Optional[str] = "runtime_error",
param: Optional[str] = None,
code: Optional[str] = None,
display_message: Optional[str] = None,
headers: Optional[Dict[str, str]] = None,
) -> None:
super().__init__(message)
status_code = int(status_code)
self.message = message
self.status_code = status_code
self.type = type
self.param = param
self.code = code or str(status_code)
self.display_message = display_message
self.headers = headers
def __repr__(self):
# headers field is omitted deliberately
# since it may contain sensitive information
return (
"%s(message=%r, status_code=%r, type=%r, param=%r, code=%r, display_message=%r)"
% (
self.__class__.__name__,
self.message,
self.status_code,
self.type,
self.param,
self.code,
self.display_message,
)
)
def json_error(self) -> dict:
return {
"error": remove_nones(
{
"message": self.message,
"type": self.type,
"param": self.param,
"code": self.code,
"display_message": self.display_message,
}
)
}
def to_fastapi_response(self) -> JSONResponse:
return JSONResponse(
status_code=self.status_code,
content=self.json_error(),
headers=self.headers,
)
def to_fastapi_exception(self) -> FastAPIException:
return FastAPIException(
status_code=self.status_code,
detail=self.json_error(),
headers=self.headers,
)
class ResourceNotFoundError(HTTPException):
"""
Thrown by OpenAI when either endpoint is invalid or api-version is unknown
"""
def __init__(self, message: str, **kwargs) -> None:
super().__init__(
status_code=HTTPStatus.NOT_FOUND,
message=message,
**kwargs,
)
class DeploymentNotFoundError(HTTPException):
"""
Thrown by OpenAI when the deployment isn't found
"""
def __init__(self, message: str, **kwargs) -> None:
super().__init__(
status_code=HTTPStatus.NOT_FOUND,
code="DeploymentNotFound",
message=message,
**kwargs,
)
class RequestValidationError(HTTPException):
def __init__(self, message: str, **kwargs) -> None:
return super().__init__(
status_code=HTTPStatus.UNPROCESSABLE_ENTITY,
type="invalid_request_error",
message=message,
**kwargs,
)
class InvalidRequestError(HTTPException):
def __init__(self, message: str, **kwargs) -> None:
return super().__init__(
status_code=HTTPStatus.BAD_REQUEST,
type="invalid_request_error",
message=message,
**kwargs,
)
class ContextLengthExceededError(InvalidRequestError):
"""
The error message that Azure OpenAI returns when the context length is exceeded.
"""
def __init__(self, max_context_length: int, prompt_tokens: int) -> None:
message = (
f"This model's maximum context length is {max_context_length} tokens. "
f"However, your messages resulted in {prompt_tokens} tokens. "
"Please reduce the length of the messages."
)
return super().__init__(
message=message,
code="context_length_exceeded",
param="messages",
)
class _TruncatePromptError(InvalidRequestError):
def __init__(self, message: str, **kwargs) -> None:
return super().__init__(
message=message,
code="truncate_prompt_error",
param="max_prompt_tokens",
**kwargs,
)
class TruncatePromptSystemError(_TruncatePromptError):
"""
The error message mimics the one of `ContextLengthExceededError`.
"""
def __init__(self, max_prompt_tokens: int, prompt_tokens: int) -> None:
message = (
f"The requested maximum prompt tokens is {max_prompt_tokens}. "
f"However, the system messages resulted in {prompt_tokens} tokens. "
"Please reduce the length of the system messages or increase the maximum prompt tokens."
)
return super().__init__(message=message, display_message=message)
class TruncatePromptSystemAndLastUserError(_TruncatePromptError):
def __init__(self, max_prompt_tokens: int, prompt_tokens: int) -> None:
message = (
f"The requested maximum prompt tokens is {max_prompt_tokens}. "
f"However, the system messages and the last user message resulted in {prompt_tokens} tokens. "
"Please reduce the length of the messages or increase the maximum prompt tokens."
)
return super().__init__(message=message, display_message=message)
class RuntimeServerError(HTTPException):
def __init__(self, message: str, **kwargs) -> None:
return super().__init__(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
type="runtime_error",
message=message,
**kwargs,
)
class InternalServerError(HTTPException):
def __init__(self, message: str, **kwargs) -> None:
return super().__init__(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
type="internal_server_error",
message=message,
**kwargs,
)
def _deprecated(ctor):
@functools.wraps(ctor)
def wrapped(*args, **kwargs):
warnings.warn(
"The helper method is deprecated. "
f"Use {ctor.__name__} class constructor directly.",
DeprecationWarning,
stacklevel=2,
)
return ctor(*args, **kwargs)
return wrapped
resource_not_found_error = _deprecated(ResourceNotFoundError)
deployment_not_found_error = _deprecated(DeploymentNotFoundError)
request_validation_error = _deprecated(RequestValidationError)
invalid_request_error = _deprecated(InvalidRequestError)
context_length_exceeded_error = _deprecated(ContextLengthExceededError)
truncate_prompt_error_system = _deprecated(TruncatePromptSystemError)
truncate_prompt_error_system_and_last_user = _deprecated(
TruncatePromptSystemAndLastUserError
)
runtime_server_error = _deprecated(RuntimeServerError)
internal_server_error = _deprecated(InternalServerError)