1#[cfg(feature = "server")]
4use axum::http::StatusCode;
5#[cfg(feature = "server")]
6use axum::response::{IntoResponse, Response};
7#[cfg(feature = "server")]
8use axum::Json;
9#[cfg(feature = "server")]
10use serde_json::json;
11use thiserror::Error;
12
13#[derive(Debug, Error)]
14pub enum GatewayError {
15 #[error("unknown model alias '{0}'")]
16 UnknownModel(String),
17 #[error("native feature '{feature}' is not available on route '{route}'")]
18 NativeFeatureUnsupported { feature: String, route: String },
19 #[error("invalid request: {0}")]
20 BadRequest(String),
21 #[error("all legs of route '{route}' failed")]
22 AllLegsFailed {
23 route: String,
24 failures: Vec<LegFailure>,
25 },
26 #[error("all providers for route '{0}' are unavailable")]
27 AllCircuitsOpen(String),
28 #[error("upstream timed out")]
29 UpstreamTimeout,
30 #[error("upstream error {status}: {body}")]
31 Upstream { status: u16, body: String },
32}
33
34#[derive(Debug, Clone, serde::Serialize)]
35pub struct LegFailure {
36 pub provider: String,
37 pub model: String,
38 pub message: String,
39}
40
41impl GatewayError {
42 #[cfg(feature = "server")]
43 pub fn status(&self) -> StatusCode {
44 match self {
45 GatewayError::UnknownModel(_) => StatusCode::NOT_FOUND,
46 GatewayError::NativeFeatureUnsupported { .. } | GatewayError::BadRequest(_) => {
47 StatusCode::BAD_REQUEST
48 }
49 GatewayError::AllLegsFailed { .. } | GatewayError::Upstream { .. } => {
50 StatusCode::BAD_GATEWAY
51 }
52 GatewayError::AllCircuitsOpen(_) => StatusCode::SERVICE_UNAVAILABLE,
53 GatewayError::UpstreamTimeout => StatusCode::GATEWAY_TIMEOUT,
54 }
55 }
56
57 #[cfg(feature = "server")]
58 fn code(&self) -> &'static str {
59 match self {
60 GatewayError::UnknownModel(_) => "model_not_found",
61 GatewayError::NativeFeatureUnsupported { .. } => "native_feature_unsupported",
62 GatewayError::BadRequest(_) => "invalid_request_error",
63 GatewayError::AllLegsFailed { .. } => "all_legs_failed",
64 GatewayError::AllCircuitsOpen(_) => "circuit_open",
65 GatewayError::UpstreamTimeout => "upstream_timeout",
66 GatewayError::Upstream { .. } => "upstream_error",
67 }
68 }
69}
70
71#[cfg(feature = "server")]
72impl IntoResponse for GatewayError {
73 fn into_response(self) -> Response {
74 let mut error = json!({
75 "type": self.code(),
76 "message": self.to_string(),
77 "code": self.code(),
78 });
79 if let GatewayError::AllLegsFailed { failures, .. } = &self {
80 error["failures"] = json!(failures);
81 }
82 (self.status(), Json(json!({ "error": error }))).into_response()
83 }
84}