2026-03-21 12:57:09 +08:00
|
|
|
use axum::body::Body;
|
|
|
|
|
use axum::http::{Response, StatusCode};
|
|
|
|
|
use axum::response::IntoResponse;
|
|
|
|
|
use axum::Json;
|
|
|
|
|
use serde_json::json;
|
|
|
|
|
use tracing::warn;
|
|
|
|
|
|
2026-05-02 13:23:54 +08:00
|
|
|
use crate::ai_serving::AiSurfaceFinalizeError;
|
2026-04-05 20:23:16 +08:00
|
|
|
use crate::constants::*;
|
|
|
|
|
use crate::insert_header_if_missing;
|
2026-03-21 12:57:09 +08:00
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub(crate) enum GatewayError {
|
2026-05-22 23:57:38 +08:00
|
|
|
UpstreamUnavailable {
|
|
|
|
|
trace_id: String,
|
|
|
|
|
message: String,
|
|
|
|
|
},
|
|
|
|
|
ControlUnavailable {
|
|
|
|
|
trace_id: String,
|
|
|
|
|
message: String,
|
|
|
|
|
},
|
|
|
|
|
LocalExecutionPlanningTimeout {
|
|
|
|
|
trace_id: String,
|
|
|
|
|
phase: &'static str,
|
|
|
|
|
timeout_ms: u64,
|
|
|
|
|
},
|
|
|
|
|
Client {
|
|
|
|
|
status: StatusCode,
|
|
|
|
|
message: String,
|
|
|
|
|
},
|
2026-03-21 12:57:09 +08:00
|
|
|
Internal(String),
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 23:57:38 +08:00
|
|
|
impl GatewayError {
|
|
|
|
|
pub(crate) fn into_message(self) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
Self::UpstreamUnavailable { message, .. }
|
|
|
|
|
| Self::ControlUnavailable { message, .. }
|
|
|
|
|
| Self::Client { message, .. }
|
|
|
|
|
| Self::Internal(message) => message,
|
|
|
|
|
Self::LocalExecutionPlanningTimeout {
|
|
|
|
|
phase, timeout_ms, ..
|
|
|
|
|
} => {
|
|
|
|
|
format!("local execution planning timed out in {phase} after {timeout_ms}ms")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 12:57:09 +08:00
|
|
|
impl IntoResponse for GatewayError {
|
|
|
|
|
fn into_response(self) -> Response<Body> {
|
|
|
|
|
match self {
|
|
|
|
|
Self::UpstreamUnavailable { trace_id, message } => {
|
2026-04-04 01:40:24 +08:00
|
|
|
warn!(trace_id = %trace_id, error = %message, "gateway proxy unavailable");
|
2026-03-21 12:57:09 +08:00
|
|
|
let body = Json(json!({
|
|
|
|
|
"error": {
|
2026-04-04 01:40:24 +08:00
|
|
|
"message": "gateway proxy unavailable",
|
2026-03-21 12:57:09 +08:00
|
|
|
"trace_id": trace_id,
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
let mut response = (StatusCode::BAD_GATEWAY, body).into_response();
|
|
|
|
|
let _ =
|
|
|
|
|
insert_header_if_missing(response.headers_mut(), TRACE_ID_HEADER, &trace_id);
|
|
|
|
|
let _ = insert_header_if_missing(
|
|
|
|
|
response.headers_mut(),
|
|
|
|
|
GATEWAY_HEADER,
|
|
|
|
|
"rust-phase3b",
|
|
|
|
|
);
|
|
|
|
|
response
|
|
|
|
|
}
|
|
|
|
|
Self::ControlUnavailable { trace_id, message } => {
|
|
|
|
|
warn!(trace_id = %trace_id, error = %message, "gateway control unavailable");
|
|
|
|
|
let body = Json(json!({
|
|
|
|
|
"error": {
|
|
|
|
|
"message": "gateway control unavailable",
|
|
|
|
|
"trace_id": trace_id,
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
let mut response = (StatusCode::BAD_GATEWAY, body).into_response();
|
|
|
|
|
let _ =
|
|
|
|
|
insert_header_if_missing(response.headers_mut(), TRACE_ID_HEADER, &trace_id);
|
2026-05-22 23:57:38 +08:00
|
|
|
let _ = insert_header_if_missing(
|
|
|
|
|
response.headers_mut(),
|
|
|
|
|
GATEWAY_HEADER,
|
|
|
|
|
"rust-phase3b",
|
|
|
|
|
);
|
|
|
|
|
response
|
|
|
|
|
}
|
|
|
|
|
Self::LocalExecutionPlanningTimeout {
|
|
|
|
|
trace_id,
|
|
|
|
|
phase,
|
|
|
|
|
timeout_ms,
|
|
|
|
|
} => {
|
|
|
|
|
warn!(
|
|
|
|
|
trace_id = %trace_id,
|
|
|
|
|
phase,
|
|
|
|
|
timeout_ms,
|
|
|
|
|
"gateway local execution planning timed out"
|
|
|
|
|
);
|
|
|
|
|
let body = Json(json!({
|
|
|
|
|
"error": {
|
|
|
|
|
"message": "gateway local execution planning timed out",
|
|
|
|
|
"trace_id": trace_id,
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
let mut response = (StatusCode::GATEWAY_TIMEOUT, body).into_response();
|
|
|
|
|
let _ =
|
|
|
|
|
insert_header_if_missing(response.headers_mut(), TRACE_ID_HEADER, &trace_id);
|
2026-03-21 12:57:09 +08:00
|
|
|
let _ = insert_header_if_missing(
|
|
|
|
|
response.headers_mut(),
|
|
|
|
|
GATEWAY_HEADER,
|
|
|
|
|
"rust-phase3b",
|
|
|
|
|
);
|
|
|
|
|
response
|
|
|
|
|
}
|
2026-05-13 18:25:13 +08:00
|
|
|
Self::Client { status, message } => (
|
|
|
|
|
status,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": {
|
|
|
|
|
"message": message,
|
|
|
|
|
}
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.into_response(),
|
2026-03-21 12:57:09 +08:00
|
|
|
Self::Internal(message) => (
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": {
|
|
|
|
|
"message": message,
|
|
|
|
|
}
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-07 02:50:19 +08:00
|
|
|
|
2026-05-02 13:23:54 +08:00
|
|
|
impl From<AiSurfaceFinalizeError> for GatewayError {
|
|
|
|
|
fn from(error: AiSurfaceFinalizeError) -> Self {
|
2026-04-07 02:50:19 +08:00
|
|
|
GatewayError::Internal(error.0)
|
|
|
|
|
}
|
|
|
|
|
}
|