use std::sync::LazyLock;
use axum::http::StatusCode;
use dynamo_runtime::config::environment_names::llm as env_llm;
use thiserror::Error;
pub(crate) fn overload_status_code() -> StatusCode {
static CODE: LazyLock<StatusCode> = LazyLock::new(|| {
let default = StatusCode::from_u16(529).expect("529 is a valid HTTP status code");
std::env::var(env_llm::DYN_HTTP_OVERLOAD_STATUS_CODE)
.ok()
.and_then(|s| s.trim().parse::<u16>().ok())
.and_then(|n| StatusCode::from_u16(n).ok())
.unwrap_or(default)
});
*CODE
}
#[derive(Debug, Error)]
#[error("HTTP Error {code}: {message}")]
pub struct HttpError {
pub code: u16,
pub message: String,
}
#[derive(Debug, Clone, Copy)]
pub enum SanitizedError {
Cancelled,
Overloaded,
Unavailable,
Internal,
PreserveServerError(StatusCode),
}
impl SanitizedError {
pub fn for_backend_status(status: StatusCode) -> Option<Self> {
if status.as_u16() == 499 {
Some(SanitizedError::Cancelled)
} else if status.is_client_error() {
None
} else if status.is_server_error() {
Some(SanitizedError::PreserveServerError(status))
} else {
Some(SanitizedError::Internal)
}
}
pub fn status(self) -> StatusCode {
match self {
SanitizedError::Cancelled => StatusCode::from_u16(499).unwrap(),
SanitizedError::Overloaded => overload_status_code(),
SanitizedError::Unavailable => StatusCode::SERVICE_UNAVAILABLE,
SanitizedError::Internal => StatusCode::INTERNAL_SERVER_ERROR,
SanitizedError::PreserveServerError(code) => {
debug_assert!(
code.is_server_error(),
"PreserveServerError requires a 5xx status; got {code}"
);
code
}
}
}
pub fn anthropic_type(self) -> &'static str {
match self {
SanitizedError::Cancelled => "request_cancelled",
SanitizedError::Overloaded => "overloaded_error",
SanitizedError::Unavailable => "overloaded_error",
SanitizedError::Internal => "api_error",
SanitizedError::PreserveServerError(status) => match status.as_u16() {
503 | 529 => "overloaded_error",
_ => "api_error",
},
}
}
pub fn openai_type_slug(self) -> &'static str {
match self {
SanitizedError::Cancelled => "request_cancelled",
SanitizedError::Overloaded => "service_unavailable",
SanitizedError::Unavailable => "service_unavailable",
SanitizedError::Internal => "internal_server_error",
SanitizedError::PreserveServerError(status) => match status.as_u16() {
503 | 529 => "service_unavailable",
_ => "internal_server_error",
},
}
}
pub fn log_as_error(self) -> bool {
!matches!(self, SanitizedError::Cancelled)
}
}
impl std::fmt::Display for SanitizedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SanitizedError::Cancelled => f.write_str("Request cancelled"),
SanitizedError::Overloaded => f.write_str("Service temporarily overloaded"),
SanitizedError::Unavailable => f.write_str("Service temporarily unavailable"),
SanitizedError::Internal | SanitizedError::PreserveServerError(_) => {
f.write_str("Internal server error")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn local_statuses_distinguish_overload_from_unavailable() {
assert_eq!(SanitizedError::Overloaded.status().as_u16(), 529);
assert_eq!(
SanitizedError::Unavailable.status(),
StatusCode::SERVICE_UNAVAILABLE
);
}
#[test]
fn preserve_server_error_503_maps_to_overload_types() {
let err = SanitizedError::PreserveServerError(StatusCode::SERVICE_UNAVAILABLE);
assert_eq!(err.anthropic_type(), "overloaded_error");
assert_eq!(err.openai_type_slug(), "service_unavailable");
}
#[test]
fn preserve_server_error_529_maps_to_overload_types() {
let err = SanitizedError::PreserveServerError(StatusCode::from_u16(529).unwrap());
assert_eq!(err.anthropic_type(), "overloaded_error");
assert_eq!(err.openai_type_slug(), "service_unavailable");
}
#[test]
fn preserve_server_error_500_remains_generic() {
let err = SanitizedError::PreserveServerError(StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(err.anthropic_type(), "api_error");
assert_eq!(err.openai_type_slug(), "internal_server_error");
}
#[test]
fn for_backend_status_classifies_correctly() {
assert!(matches!(
SanitizedError::for_backend_status(StatusCode::from_u16(499).unwrap()),
Some(SanitizedError::Cancelled)
));
assert!(matches!(
SanitizedError::for_backend_status(StatusCode::SERVICE_UNAVAILABLE),
Some(SanitizedError::PreserveServerError(s)) if s == StatusCode::SERVICE_UNAVAILABLE
));
assert!(SanitizedError::for_backend_status(StatusCode::BAD_REQUEST).is_none());
assert!(SanitizedError::for_backend_status(StatusCode::NOT_FOUND).is_none());
assert!(matches!(
SanitizedError::for_backend_status(StatusCode::from_u16(399).unwrap()),
Some(SanitizedError::Internal)
));
}
}